26
common/Database/CustomLengthAwarePaginator.php
Executable file
26
common/Database/CustomLengthAwarePaginator.php
Executable file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database;
|
||||
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
|
||||
class CustomLengthAwarePaginator extends LengthAwarePaginator
|
||||
{
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'current_page' => $this->currentPage(),
|
||||
'data' => $this->items->toArray(),
|
||||
'from' => $this->firstItem(),
|
||||
'last_page' => $this->lastPage(),
|
||||
'next_page' => $this->hasMorePages()
|
||||
? $this->currentPage() + 1
|
||||
: null,
|
||||
'per_page' => $this->perPage(),
|
||||
'prev_page' =>
|
||||
$this->currentPage() > 1 ? $this->currentPage() - 1 : null,
|
||||
'to' => $this->lastItem(),
|
||||
'total' => $this->total(),
|
||||
];
|
||||
}
|
||||
}
|
||||
22
common/Database/CustomSimplePaginator.php
Executable file
22
common/Database/CustomSimplePaginator.php
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database;
|
||||
|
||||
class CustomSimplePaginator extends \Illuminate\Pagination\Paginator
|
||||
{
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'current_page' => $this->currentPage(),
|
||||
'data' => $this->items->toArray(),
|
||||
'from' => $this->firstItem(),
|
||||
'next_page' => $this->hasMorePages()
|
||||
? $this->currentPage() + 1
|
||||
: null,
|
||||
'per_page' => $this->perPage(),
|
||||
'prev_page' =>
|
||||
$this->currentPage() > 1 ? $this->currentPage() - 1 : null,
|
||||
'to' => $this->lastItem(),
|
||||
];
|
||||
}
|
||||
}
|
||||
260
common/Database/Datasource/Datasource.php
Executable file
260
common/Database/Datasource/Datasource.php
Executable file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource;
|
||||
|
||||
use Common\Database\Datasource\Filters\AlgoliaFilterer;
|
||||
use Common\Database\Datasource\Filters\ElasticFilterer;
|
||||
use Common\Database\Datasource\Filters\MeilisearchFilterer;
|
||||
use Common\Database\Datasource\Filters\MysqlFilterer;
|
||||
use Common\Database\Datasource\Filters\TntFilterer;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use Illuminate\Pagination\AbstractPaginator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Scout\Builder as ScoutBuilder;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Matchish\ScoutElasticSearch\Engines\ElasticSearchEngine;
|
||||
use const App\Providers\WORKSPACED_RESOURCES;
|
||||
|
||||
class Datasource
|
||||
{
|
||||
protected EloquentBuilder|Relation $builder;
|
||||
protected Model $model;
|
||||
protected array $params;
|
||||
protected bool $queryBuilt = false;
|
||||
public DatasourceFilters $filters;
|
||||
public array|null|false $order = null;
|
||||
// sometimes we might need to order by ID or something else, if primary column
|
||||
// is not guaranteed to be unique, to avoid duplicated items in pagination
|
||||
public string|null $secondaryOrderCol = null;
|
||||
protected ?ScoutBuilder $scoutBuilder;
|
||||
|
||||
public function __construct(
|
||||
$model,
|
||||
array $params,
|
||||
DatasourceFilters $filters = null,
|
||||
protected string $filtererName = 'mysql',
|
||||
) {
|
||||
$this->model = $model->getModel();
|
||||
$this->params = $this->toCamelCase($params);
|
||||
$this->builder = $model instanceof Model ? $model->newQuery() : $model;
|
||||
$this->filters =
|
||||
$filters ?? new DatasourceFilters($this->params['filters'] ?? null);
|
||||
}
|
||||
|
||||
public function paginate(): AbstractPaginator
|
||||
{
|
||||
$this->buildQuery();
|
||||
$perPage = $this->limit();
|
||||
$page = (int) $this->param('page', 1);
|
||||
$columns = empty($this->builder->getQuery()->columns)
|
||||
? ['*']
|
||||
: $this->builder->getQuery()->columns;
|
||||
|
||||
if ($this->param('paginate') === 'lengthAware') {
|
||||
return $this->scoutBuilder instanceof ScoutBuilder
|
||||
? $this->scoutBuilder->paginate($perPage, 'page', $page)
|
||||
: $this->builder->paginate($perPage, $columns, 'page', $page);
|
||||
} else {
|
||||
return $this->scoutBuilder instanceof ScoutBuilder
|
||||
? $this->scoutBuilder->simplePaginate($perPage, 'page', $page)
|
||||
: $this->builder->simplePaginate(
|
||||
$perPage,
|
||||
$columns,
|
||||
'page',
|
||||
$page,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function get(): Collection
|
||||
{
|
||||
$this->buildQuery();
|
||||
return $this->builder->limit($this->limit())->get();
|
||||
}
|
||||
|
||||
public function param(string $name, $default = null)
|
||||
{
|
||||
return Arr::get($this->params, Str::camel($name)) ?: $default;
|
||||
}
|
||||
|
||||
public function buildQuery(): self
|
||||
{
|
||||
if ($this->queryBuilt) {
|
||||
return $this;
|
||||
}
|
||||
$with = array_filter(explode(',', $this->param('with', '')));
|
||||
$withCount = array_filter(explode(',', $this->param('withCount', '')));
|
||||
$searchTerm = $this->param('query');
|
||||
|
||||
// load specified relations and counts
|
||||
if (!empty($with)) {
|
||||
$this->builder->with($with);
|
||||
}
|
||||
if (!empty($withCount)) {
|
||||
$this->builder->withCount($withCount);
|
||||
}
|
||||
|
||||
$this->applyWorkspaceFilter();
|
||||
|
||||
$filterer = $this->resolveFilterer($searchTerm);
|
||||
$this->scoutBuilder = (new $filterer(
|
||||
$this->builder,
|
||||
$this->filters,
|
||||
$searchTerm,
|
||||
))->apply();
|
||||
|
||||
// allow caller class to override order or
|
||||
// prevent it completely by setting "false"
|
||||
if ($this->order !== false) {
|
||||
$order = $this->getOrder();
|
||||
if (isset($order['col'])) {
|
||||
$orderCol = str_replace(
|
||||
$this->builder->getModel()->getTable() . '.',
|
||||
'',
|
||||
$order['col'],
|
||||
);
|
||||
$methodName = Str::camel('orderBy' . ucfirst($orderCol));
|
||||
$scopeMethodName = 'scope' . ucfirst($methodName);
|
||||
if (
|
||||
method_exists($this->builder->getModel(), $methodName) ||
|
||||
method_exists($this->builder->getModel(), $scopeMethodName)
|
||||
) {
|
||||
$this->builder->$methodName($order['dir']);
|
||||
} else {
|
||||
$this->builder->orderBy(
|
||||
Str::snake($order['col']),
|
||||
$order['dir'] ?? 'desc',
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->secondaryOrderCol) {
|
||||
$this->builder->orderBy(
|
||||
$this->secondaryOrderCol,
|
||||
$order['dir'] ?? 'desc',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->queryBuilt = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function resolveFilterer(string $searchTerm = null): string
|
||||
{
|
||||
$filtererName = $this->filtererName;
|
||||
if (
|
||||
!$searchTerm ||
|
||||
!in_array(Searchable::class, class_uses_recursive($this->model)) ||
|
||||
$filtererName === 'mysql'
|
||||
) {
|
||||
return MysqlFilterer::class;
|
||||
} elseif ($filtererName === 'meilisearch') {
|
||||
return MeilisearchFilterer::class;
|
||||
} elseif ($filtererName === 'tntsearch') {
|
||||
return TntFilterer::class;
|
||||
} elseif ($filtererName === 'algolia') {
|
||||
return AlgoliaFilterer::class;
|
||||
} elseif ($filtererName === ElasticSearchEngine::class) {
|
||||
return ElasticFilterer::class;
|
||||
}
|
||||
|
||||
return MysqlFilterer::class;
|
||||
}
|
||||
|
||||
private function applyWorkspaceFilter(): void
|
||||
{
|
||||
if (
|
||||
!config('common.site.workspaces_integrated') ||
|
||||
!in_array(get_class($this->model), WORKSPACED_RESOURCES)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($workspaceId = $this->param('workspaceId')) {
|
||||
$this->filters->where('workspace_id', '=', $workspaceId);
|
||||
} elseif ($userId = $this->param('userId')) {
|
||||
$this->filters
|
||||
->where('user_id', '=', $userId)
|
||||
->where('workspace_id', '=', 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function getOrder(
|
||||
string $defaultOrderCol = 'updated_at',
|
||||
string $defaultOrderDir = 'desc',
|
||||
): array {
|
||||
if (isset($this->order['col'])) {
|
||||
$orderCol = $this->order['col'];
|
||||
$orderDir = $this->order['dir'];
|
||||
// order might be a single string: "column|direction"
|
||||
} elseif ($specifiedOrder = $this->param('order')) {
|
||||
$parts = preg_split('(\||:)', $specifiedOrder);
|
||||
$orderCol = Arr::get($parts, 0, $defaultOrderCol);
|
||||
$orderDir = Arr::get($parts, 1, $defaultOrderDir);
|
||||
// order might be as separate params
|
||||
} elseif ($this->param('orderBy') || $this->param('orderDir')) {
|
||||
$orderCol = $this->param('orderBy');
|
||||
$orderDir = $this->param('orderDir');
|
||||
// try ordering be relevance, if it's a search query and
|
||||
// using mysql fulltext, finally default to "updated_at" column
|
||||
} elseif ($this->hasRelevanceColumn()) {
|
||||
$orderCol = 'relevance';
|
||||
$orderDir = 'desc';
|
||||
} else {
|
||||
$orderCol = $defaultOrderCol;
|
||||
$orderDir = $defaultOrderDir;
|
||||
}
|
||||
|
||||
if ($orderCol !== 'relevance' && !Str::endsWith($orderCol, '_count')) {
|
||||
$orderCol = $this->builder->qualifyColumn($orderCol);
|
||||
}
|
||||
|
||||
return [
|
||||
'col' => $orderCol,
|
||||
'dir' => $orderDir,
|
||||
];
|
||||
}
|
||||
|
||||
private function toCamelCase(array $params): array
|
||||
{
|
||||
return collect($params)
|
||||
->keyBy(function ($value, $key) {
|
||||
return Str::camel($key);
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private function hasRelevanceColumn(): bool
|
||||
{
|
||||
return !!Arr::first($this->getQueryBuilder() ?? [], function ($col) {
|
||||
return $col instanceof Expression &&
|
||||
Str::endsWith($col->getValue(), 'AS relevance');
|
||||
});
|
||||
}
|
||||
|
||||
private function limit(): int
|
||||
{
|
||||
if ($this->param('perPage')) {
|
||||
return (int) $this->param('perPage');
|
||||
} else {
|
||||
return $this->getQueryBuilder()->limit ?? 15;
|
||||
}
|
||||
}
|
||||
|
||||
private function getQueryBuilder(): QueryBuilder
|
||||
{
|
||||
$query = $this->builder->getQuery();
|
||||
if ($query instanceof EloquentBuilder) {
|
||||
$query = $query->getQuery();
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
131
common/Database/Datasource/DatasourceFilters.php
Executable file
131
common/Database/Datasource/DatasourceFilters.php
Executable file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource;
|
||||
|
||||
use Auth;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class DatasourceFilters
|
||||
{
|
||||
private array $filters;
|
||||
|
||||
public function __construct(?string $encodedFilters = '')
|
||||
{
|
||||
$this->filters = $this->decodeFilters($encodedFilters);
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function empty(): bool
|
||||
{
|
||||
return empty($this->filters);
|
||||
}
|
||||
|
||||
public function has(string $key): bool {
|
||||
foreach ($this->filters as $filter) {
|
||||
if ($filter['key'] === $key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function where(string $key, string $operator, $value): self
|
||||
{
|
||||
if ($value instanceof Collection) {
|
||||
$value = $value->toArray();
|
||||
}
|
||||
$this->filters[] = [
|
||||
'key' => $key,
|
||||
'operator' => $operator,
|
||||
'value' => $value,
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAndRemove(
|
||||
string $key,
|
||||
string $operator = null,
|
||||
$value = null,
|
||||
): ?array {
|
||||
// use func_get_args as "null" is a valid value, need
|
||||
// to check whether if it was actually passed by user
|
||||
$args = func_get_args();
|
||||
|
||||
foreach ($this->filters as $key => $filter) {
|
||||
if (
|
||||
$filter['key'] === $args[0] &&
|
||||
(!isset($args[1]) || $filter['operator'] === $args[1]) &&
|
||||
(!isset($args[2]) || $filter['value'] === $args[2])
|
||||
) {
|
||||
unset($this->filters[$key]);
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function decodeFilters(?string $filterString): array
|
||||
{
|
||||
if ($filterString) {
|
||||
$filters = json_decode(
|
||||
base64_decode(urldecode($filterString)),
|
||||
true,
|
||||
);
|
||||
return collect($filters)
|
||||
->flatMap(fn($filter) => $this->normalizeFilter($filter))
|
||||
->filter()
|
||||
->toArray();
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeFilter(array $filter): ?array
|
||||
{
|
||||
$value = $filter['value'] ?? null;
|
||||
$operator = $filter['operator'] ?? '=';
|
||||
|
||||
$value = $this->replaceValuePlaceholders($value);
|
||||
|
||||
if (is_array($value)) {
|
||||
// filtering by normalized model
|
||||
if (isset($value['id'])) {
|
||||
$value = $this->replaceValuePlaceholders($value['id']);
|
||||
|
||||
// "value" contains both value and operator
|
||||
} elseif (array_key_exists('value', $value)) {
|
||||
$operator = $value['operator'] ?? $operator;
|
||||
$value = $this->replaceValuePlaceholders($value['value']);
|
||||
}
|
||||
}
|
||||
|
||||
$filters = [
|
||||
// preserve any extra keys that might be present
|
||||
array_merge($filter, [
|
||||
'key' => $filter['key'],
|
||||
'operator' => $operator,
|
||||
'value' => $value,
|
||||
]),
|
||||
];
|
||||
|
||||
// filter might have some extra static filters, for example to restrict by model type
|
||||
if (isset($filter['extraFilters'])) {
|
||||
$filters[] = $filter['extraFilters'];
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
private function replaceValuePlaceholders($value)
|
||||
{
|
||||
if ($value === '{authId}') {
|
||||
return Auth::id();
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
54
common/Database/Datasource/Filters/AlgoliaFilterer.php
Executable file
54
common/Database/Datasource/Filters/AlgoliaFilterer.php
Executable file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource\Filters;
|
||||
|
||||
use Algolia\AlgoliaSearch\SearchIndex;
|
||||
use Common\Database\Datasource\Filters\Traits\NormalizesFiltersForFulltextEngines;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Scout\Builder;
|
||||
|
||||
class AlgoliaFilterer extends BaseFilterer
|
||||
{
|
||||
use NormalizesFiltersForFulltextEngines;
|
||||
|
||||
public function apply(): ?Builder
|
||||
{
|
||||
return $this->query
|
||||
->getModel()
|
||||
->search($this->searchTerm, function (
|
||||
SearchIndex $algolia,
|
||||
string $query,
|
||||
array $options
|
||||
) {
|
||||
$filters = $this->prepareFiltersForAlgolia();
|
||||
$filters = implode(' AND ', $filters);
|
||||
if ($filters) {
|
||||
$options['filters'] = $filters;
|
||||
}
|
||||
return $algolia->search($query, $options);
|
||||
});
|
||||
}
|
||||
|
||||
private function prepareFiltersForAlgolia(): array
|
||||
{
|
||||
$filters = $this->normalizeFilters($this->filters->getAll());
|
||||
return array_map(function ($filter) {
|
||||
$prefix = '';
|
||||
|
||||
if (Str::contains($filter['operator'], '!')) {
|
||||
$filter['operator'] = str_replace('!', '', $filter['operator']);
|
||||
$prefix = 'NOT ';
|
||||
}
|
||||
|
||||
if (!is_numeric($filter['value']) && $filter['operator'] === '=') {
|
||||
$filter['operator'] = ':';
|
||||
}
|
||||
|
||||
if (is_array($filter['value'])) {
|
||||
$filter['value'] = implode(',', $filter['value']);
|
||||
}
|
||||
|
||||
return $prefix . implode('', $filter);
|
||||
}, $filters);
|
||||
}
|
||||
}
|
||||
18
common/Database/Datasource/Filters/BaseFilterer.php
Executable file
18
common/Database/Datasource/Filters/BaseFilterer.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource\Filters;
|
||||
|
||||
use Common\Database\Datasource\DatasourceFilters;
|
||||
use Laravel\Scout\Builder as ScoutBuilder;
|
||||
|
||||
abstract class BaseFilterer
|
||||
{
|
||||
public function __construct(
|
||||
protected $query,
|
||||
protected DatasourceFilters $filters,
|
||||
protected string|null $searchTerm = null
|
||||
) {
|
||||
}
|
||||
|
||||
abstract public function apply(): ?ScoutBuilder;
|
||||
}
|
||||
47
common/Database/Datasource/Filters/ElasticFilterer.php
Executable file
47
common/Database/Datasource/Filters/ElasticFilterer.php
Executable file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource\Filters;
|
||||
|
||||
use Common\Database\Datasource\Filters\Traits\NormalizesFiltersForFulltextEngines;
|
||||
use Elasticsearch\Client;
|
||||
use Laravel\Scout\Builder;
|
||||
use Matchish\ScoutElasticSearch\ElasticSearch\Params\Search as SearchParams;
|
||||
use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery;
|
||||
use ONGR\ElasticsearchDSL\Query\TermLevel\TermsQuery;
|
||||
use ONGR\ElasticsearchDSL\Search;
|
||||
|
||||
class ElasticFilterer extends BaseFilterer
|
||||
{
|
||||
use NormalizesFiltersForFulltextEngines;
|
||||
|
||||
public function apply(): ?Builder
|
||||
{
|
||||
return $this->query
|
||||
->getModel()
|
||||
->search($this->searchTerm, function (
|
||||
Client $client,
|
||||
Search $body
|
||||
) {
|
||||
$filters = $this->normalizeFilters($this->filters->getAll());
|
||||
foreach ($filters as $filter) {
|
||||
if (is_array($filter['value'])) {
|
||||
$filter = new TermsQuery(
|
||||
$filter['key'],
|
||||
$filter['value'],
|
||||
);
|
||||
} else {
|
||||
$filter = new TermQuery(
|
||||
$filter['key'],
|
||||
$filter['value'],
|
||||
);
|
||||
}
|
||||
$body->addPostFilter($filter);
|
||||
}
|
||||
$params = new SearchParams(
|
||||
$this->query->getModel()->searchableAs(),
|
||||
$body->toArray(),
|
||||
);
|
||||
return $client->search($params->toArray());
|
||||
});
|
||||
}
|
||||
}
|
||||
61
common/Database/Datasource/Filters/MeilisearchFilterer.php
Executable file
61
common/Database/Datasource/Filters/MeilisearchFilterer.php
Executable file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource\Filters;
|
||||
|
||||
use Common\Database\Datasource\Filters\Traits\NormalizesFiltersForFulltextEngines;
|
||||
use Laravel\Scout\Builder;
|
||||
|
||||
class MeilisearchFilterer extends BaseFilterer
|
||||
{
|
||||
use NormalizesFiltersForFulltextEngines;
|
||||
|
||||
public function apply(): Builder
|
||||
{
|
||||
return $this->query
|
||||
->getModel()
|
||||
->search($this->searchTerm, function (
|
||||
$driver,
|
||||
?string $query,
|
||||
array $options
|
||||
) {
|
||||
$filters = $this->prepareFiltersForMeilisearch();
|
||||
$filters = implode(' AND ', $filters);
|
||||
if ($filters) {
|
||||
$options['filter'] = $filters;
|
||||
}
|
||||
return $driver->search($query, $options);
|
||||
});
|
||||
}
|
||||
|
||||
private function prepareFiltersForMeilisearch(): array
|
||||
{
|
||||
$filters = $this->normalizeFilters($this->filters->getAll());
|
||||
return array_map(function ($filter) {
|
||||
if (is_array($filter['value'])) {
|
||||
$values = array_map(
|
||||
fn($v) => $this->createFilterString(
|
||||
$filter['key'],
|
||||
$filter['operator'],
|
||||
$v,
|
||||
),
|
||||
$filter['value'],
|
||||
);
|
||||
return '(' . implode(' OR ', $values) . ')';
|
||||
} else {
|
||||
return $this->createFilterString(
|
||||
$filter['key'],
|
||||
$filter['operator'],
|
||||
$filter['value'],
|
||||
);
|
||||
}
|
||||
}, $filters);
|
||||
}
|
||||
|
||||
private function createFilterString(
|
||||
string $key,
|
||||
string $operator,
|
||||
$value
|
||||
): string {
|
||||
return "$key $operator $value";
|
||||
}
|
||||
}
|
||||
22
common/Database/Datasource/Filters/MysqlFilterer.php
Executable file
22
common/Database/Datasource/Filters/MysqlFilterer.php
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource\Filters;
|
||||
|
||||
use Common\Database\Datasource\Filters\Traits\SupportsMysqlFilters;
|
||||
use Laravel\Scout\Builder;
|
||||
|
||||
class MysqlFilterer extends BaseFilterer
|
||||
{
|
||||
use SupportsMysqlFilters;
|
||||
|
||||
public function apply(): ?Builder
|
||||
{
|
||||
$this->applyMysqlFilters($this->filters, $this->query);
|
||||
|
||||
if ($this->searchTerm) {
|
||||
$this->query->mysqlSearch($this->searchTerm);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
24
common/Database/Datasource/Filters/TntFilterer.php
Executable file
24
common/Database/Datasource/Filters/TntFilterer.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource\Filters;
|
||||
|
||||
use Common\Database\Datasource\Filters\Traits\SupportsMysqlFilters;
|
||||
use Laravel\Scout\Builder;
|
||||
|
||||
class TntFilterer extends BaseFilterer
|
||||
{
|
||||
use SupportsMysqlFilters;
|
||||
|
||||
public function apply(): ?Builder
|
||||
{
|
||||
$constrains = $this->applyMysqlFilters(
|
||||
$this->filters,
|
||||
$this->query->getModel()->newInstance(),
|
||||
);
|
||||
|
||||
return $this->query
|
||||
->getModel()
|
||||
->search($this->searchTerm)
|
||||
->constrain($constrains);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource\Filters\Traits;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait NormalizesFiltersForFulltextEngines
|
||||
{
|
||||
protected function normalizeFilters(array $filters): array
|
||||
{
|
||||
$normalizedFilters = [];
|
||||
|
||||
foreach ($filters as $index => $filter) {
|
||||
// flatten "between" filter
|
||||
if ($filter['operator'] === 'between') {
|
||||
if ($start = Arr::get($filter, 'value.start')) {
|
||||
$normalizedFilters[] = [
|
||||
'key' => $filter['key'],
|
||||
'operator' => '>=',
|
||||
'value' => Carbon::parse($start)->timestamp,
|
||||
];
|
||||
}
|
||||
if ($end = Arr::get($filter, 'value.end')) {
|
||||
$normalizedFilters[] = [
|
||||
'key' => $filter['key'],
|
||||
'operator' => '<=',
|
||||
'value' => Carbon::parse($end)->timestamp,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// normalize value and operator, so it's accepted by meilisearch, elastic and algolia
|
||||
$normalizedFilters[] = [
|
||||
'key' => $filter['key'],
|
||||
'operator' => $this->normalizeFilterOperator($filter),
|
||||
'value' => $this->normalizeFilterValue($filter),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $normalizedFilters;
|
||||
}
|
||||
|
||||
protected function normalizeFilterValue(array $filter): array|string
|
||||
{
|
||||
$value = $filter['value'];
|
||||
if (is_string($value) && Str::contains($value, ' ')) {
|
||||
return "'$value'";
|
||||
} elseif ($value === null) {
|
||||
return '_null';
|
||||
} elseif ($value === false) {
|
||||
return 'false';
|
||||
} elseif ($value === true) {
|
||||
return 'true';
|
||||
} elseif (
|
||||
in_array($filter['key'], $this->query->getModel()->getDates())
|
||||
) {
|
||||
return Carbon::parse($value)->timestamp;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
protected function normalizeFilterOperator(array $filter): string
|
||||
{
|
||||
if ($filter['operator'] === 'has') {
|
||||
return '=';
|
||||
} elseif ($filter['operator'] === 'doesntHave') {
|
||||
return '!=';
|
||||
} else {
|
||||
return $filter['operator'];
|
||||
}
|
||||
}
|
||||
}
|
||||
180
common/Database/Datasource/Filters/Traits/SupportsMysqlFilters.php
Executable file
180
common/Database/Datasource/Filters/Traits/SupportsMysqlFilters.php
Executable file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Datasource\Filters\Traits;
|
||||
|
||||
use Common\Database\Datasource\DatasourceFilters;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
trait SupportsMysqlFilters
|
||||
{
|
||||
public function applyMysqlFilters(DatasourceFilters $filters, $query)
|
||||
{
|
||||
foreach ($filters->getAll() as $filter) {
|
||||
if ($filter['value'] === 'null') {
|
||||
$filter['value'] = null;
|
||||
} elseif ($filter['value'] === 'false') {
|
||||
$filter['value'] = false;
|
||||
}
|
||||
if ($filter['value'] === 'true') {
|
||||
$filter['value'] = true;
|
||||
}
|
||||
|
||||
if ($filter['operator'] === 'between') {
|
||||
$query->whereBetween(
|
||||
$query->getModel()->qualifyColumn($filter['key']),
|
||||
[$filter['value']['start'], $filter['value']['end']],
|
||||
);
|
||||
} elseif (
|
||||
$filter['operator'] === 'has' ||
|
||||
$filter['operator'] === 'doesntHave'
|
||||
) {
|
||||
$relName = $filter['key'];
|
||||
$relation = $query->getModel()->$relName();
|
||||
if (
|
||||
$relation instanceof HasMany ||
|
||||
$relation instanceof HasOne
|
||||
) {
|
||||
$query = $this->filterByHasManyRelation(
|
||||
$query,
|
||||
$relation,
|
||||
$filter,
|
||||
);
|
||||
} elseif ($relation instanceof BelongsToMany) {
|
||||
$query = $this->filterByManyToManyRelation(
|
||||
$query,
|
||||
$relation,
|
||||
$filter,
|
||||
);
|
||||
}
|
||||
} elseif ($filter['operator'] === 'hasAll') {
|
||||
$genreIds = $filter['value'];
|
||||
$relName = $filter['key'];
|
||||
$relation = $query->getModel()->$relName();
|
||||
$query->whereIn(
|
||||
$query->getModel()->qualifyColumn('id'),
|
||||
fn(Builder $query) => $query
|
||||
->select($relation->getQualifiedForeignPivotKeyName())
|
||||
->from($relation->getTable())
|
||||
->whereIn(
|
||||
$relation->getQualifiedRelatedPivotKeyName(),
|
||||
$genreIds,
|
||||
)
|
||||
->when(
|
||||
count($genreIds) > 1,
|
||||
fn(Builder $query) => $query
|
||||
->groupBy(
|
||||
$relation->getQualifiedForeignPivotKeyName(),
|
||||
)
|
||||
->having(
|
||||
DB::raw('COUNT(*)'),
|
||||
'=',
|
||||
count($genreIds),
|
||||
),
|
||||
),
|
||||
);
|
||||
} elseif (
|
||||
$query->hasNamedScope('where' . ucfirst($filter['key']))
|
||||
) {
|
||||
$query->{'where' . ucfirst($filter['key'])}(
|
||||
$filter['value'],
|
||||
$filter['operator'],
|
||||
$filter['key'],
|
||||
);
|
||||
} else {
|
||||
$query = $query->where(
|
||||
$query->qualifyColumn($filter['key']),
|
||||
$filter['operator'],
|
||||
$filter['value'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function filterByHasManyRelation($query, $relation, array $filter)
|
||||
{
|
||||
$related = $relation->getRelated()->getTable();
|
||||
$foreignKey = $relation->getQualifiedForeignKeyName();
|
||||
$parentKey = $relation->getQualifiedParentKeyName();
|
||||
|
||||
// use left join to check if model has any of specified relations
|
||||
if ($filter['value'] === '*') {
|
||||
$query
|
||||
// prevent null values from being returned when using left join
|
||||
->when(empty($query->getQuery()->getColumns()), function ($q) {
|
||||
$q->select($q->getModel()->getTable() . '.*');
|
||||
})
|
||||
->leftJoin($related, $foreignKey, '=', $parentKey)
|
||||
->where(
|
||||
$foreignKey,
|
||||
$filter['operator'] === 'doesntHave' ? '=' : '!=',
|
||||
null,
|
||||
);
|
||||
// use left join to check if model has relation with specified ID
|
||||
} else {
|
||||
$query
|
||||
->leftJoin($related, $foreignKey, '=', $parentKey)
|
||||
->where(
|
||||
"$related.id",
|
||||
$filter['operator'] === 'has' ? '=' : '!=',
|
||||
$filter['value'],
|
||||
);
|
||||
if ($filter['operator'] === 'doesntHave') {
|
||||
$this->query->orWhere("$related.id", null);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function filterByManyToManyRelation(
|
||||
$query,
|
||||
$relation,
|
||||
array $filter,
|
||||
) {
|
||||
if ($filter['operator'] === 'has') {
|
||||
$values = is_array($filter['value'])
|
||||
? $filter['value']
|
||||
: [$filter['value']];
|
||||
$query->join(
|
||||
$relation->getTable(),
|
||||
$relation->getQualifiedParentKeyName(),
|
||||
'=',
|
||||
$relation->getQualifiedForeignPivotKeyName(),
|
||||
);
|
||||
|
||||
$query->where(function ($q) use ($values, $relation) {
|
||||
foreach ($values as $value) {
|
||||
$q->orWhere(
|
||||
$relation->getQualifiedRelatedPivotKeyName(),
|
||||
'=',
|
||||
$value,
|
||||
);
|
||||
}
|
||||
});
|
||||
} elseif ($filter['operator'] === 'doesntHave') {
|
||||
$table = $query->getModel()->getTable();
|
||||
$query->whereNotIn("$table.id", function (Builder $builder) use (
|
||||
$filter,
|
||||
$query,
|
||||
) {
|
||||
$relName = $filter['key'];
|
||||
$relation = $query->getModel()->$relName();
|
||||
$builder
|
||||
->select($relation->getQualifiedForeignPivotKeyName())
|
||||
->from($relation->getTable())
|
||||
->where(
|
||||
$relation->getQualifiedRelatedPivotKeyName(),
|
||||
$filter['value'],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
78
common/Database/Metrics/BaseMetric.php
Executable file
78
common/Database/Metrics/BaseMetric.php
Executable file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Metrics;
|
||||
|
||||
use Common\Database\Metrics\Traits\RoundingPrecision;
|
||||
use Illuminate\Contracts\Database\Query\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
|
||||
abstract class BaseMetric
|
||||
{
|
||||
use RoundingPrecision;
|
||||
|
||||
protected Builder $query;
|
||||
|
||||
public function __construct(
|
||||
public Builder|string|Model $model,
|
||||
public MetricDateRange $dateRange,
|
||||
public string|Expression|null $column = null,
|
||||
public string|null $dateColumn = null,
|
||||
protected int $limit = 100,
|
||||
) {
|
||||
$this->query =
|
||||
$this->model instanceof Builder
|
||||
? $this->model->clone()
|
||||
: (new $this->model())->newQuery();
|
||||
|
||||
if (!$this->dateColumn) {
|
||||
$this->dateColumn = $this->query
|
||||
->getModel()
|
||||
->getQualifiedCreatedAtColumn();
|
||||
}
|
||||
}
|
||||
|
||||
public function count(): array
|
||||
{
|
||||
return $this->aggregate('count');
|
||||
}
|
||||
|
||||
public function average(): array
|
||||
{
|
||||
return $this->aggregate('avg');
|
||||
}
|
||||
|
||||
public function sum(): array
|
||||
{
|
||||
return $this->aggregate('sum');
|
||||
}
|
||||
|
||||
public function max(): array
|
||||
{
|
||||
return $this->aggregate('max');
|
||||
}
|
||||
|
||||
public function min(): array
|
||||
{
|
||||
return $this->aggregate('min');
|
||||
}
|
||||
|
||||
abstract protected function aggregate(string $function);
|
||||
|
||||
protected function getWrappedColumn(): string
|
||||
{
|
||||
$column =
|
||||
$this->column ?: $this->query->getModel()->getQualifiedKeyName();
|
||||
return $column instanceof Expression
|
||||
? (string) $column
|
||||
: $this->query
|
||||
->getQuery()
|
||||
->getGrammar()
|
||||
->wrap($column);
|
||||
}
|
||||
|
||||
protected function round(int|float $value): float
|
||||
{
|
||||
return round($value, $this->roundingPrecision, $this->roundingMode);
|
||||
}
|
||||
}
|
||||
102
common/Database/Metrics/MetricDateRange.php
Executable file
102
common/Database/Metrics/MetricDateRange.php
Executable file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Metrics;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Carbon\CarbonInterval;
|
||||
use Carbon\CarbonPeriod;
|
||||
|
||||
class MetricDateRange
|
||||
{
|
||||
const YEAR = 'year';
|
||||
const MONTH = 'month';
|
||||
const WEEK = 'week';
|
||||
const DAY = 'day';
|
||||
const HOUR = 'hour';
|
||||
const MINUTE = 'minute';
|
||||
|
||||
public CarbonImmutable $start;
|
||||
public CarbonImmutable $end;
|
||||
public string $timezone;
|
||||
public string $granularity;
|
||||
public CarbonPeriod $period;
|
||||
|
||||
public function __construct(
|
||||
string $start = null,
|
||||
string $end = null,
|
||||
string $timezone = null,
|
||||
string $granularity = null,
|
||||
) {
|
||||
$this->start = $start
|
||||
? CarbonImmutable::parse($start)->timezone($timezone)
|
||||
: CarbonImmutable::today()->startOfWeek();
|
||||
$this->end = $end
|
||||
? CarbonImmutable::parse($end)->timezone($timezone)
|
||||
: CarbonImmutable::today()->endOfWeek();
|
||||
|
||||
$this->timezone = $timezone ?: config('app.timezone');
|
||||
$this->setGranularity($granularity);
|
||||
|
||||
$this->period = CarbonPeriod::create(
|
||||
$this->start,
|
||||
$this->end,
|
||||
);
|
||||
$this->period->setDateInterval(
|
||||
CarbonInterval::make(1, $this->granularity),
|
||||
);
|
||||
}
|
||||
|
||||
public function getPreviousPeriod(): self
|
||||
{
|
||||
return new self(
|
||||
$this->start->sub($this->end->diffAsCarbonInterval($this->start)),
|
||||
$this->start->subSecond(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getCacheKey(): string
|
||||
{
|
||||
return sprintf(
|
||||
'%s-%s-%s',
|
||||
$this->start->timestamp,
|
||||
$this->end->timestamp,
|
||||
$this->timezone,
|
||||
);
|
||||
}
|
||||
|
||||
protected function setGranularity(string $granularity = null): void {
|
||||
// set unit specified by user
|
||||
if ($granularity) {
|
||||
$this->granularity = $granularity;
|
||||
// set the smallest supported unit based on start and end date
|
||||
} else {
|
||||
if ($this->start->diffInYears($this->end) >= 3) {
|
||||
$this->granularity = self::YEAR;
|
||||
} elseif ($this->start->diffInMonths($this->end) >= 4) {
|
||||
$this->granularity = self::MONTH;
|
||||
} elseif ($this->start->diffInDays($this->end) > 14) {
|
||||
$this->granularity = self::WEEK;
|
||||
} elseif ($this->start->diffInDays($this->end) > 1) {
|
||||
$this->granularity = self::DAY;
|
||||
} elseif ($this->start->diffInHours($this->end) > 1) {
|
||||
$this->granularity = self::HOUR;
|
||||
} else {
|
||||
$this->granularity = self::MINUTE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return format by which metric values should be grouped based on granularity.
|
||||
*/
|
||||
public function getGroupingFormat(): string {
|
||||
return match ($this->granularity) {
|
||||
$this::YEAR => 'Y',
|
||||
$this::MONTH => 'Y-m',
|
||||
$this::WEEK => 'o-W',
|
||||
$this::DAY => 'Y-m-d',
|
||||
$this::HOUR => 'Y-m-d H:00',
|
||||
$this::MINUTE => 'Y-m-d H:i:00',
|
||||
};
|
||||
}
|
||||
}
|
||||
76
common/Database/Metrics/Partition.php
Executable file
76
common/Database/Metrics/Partition.php
Executable file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Metrics;
|
||||
|
||||
use Illuminate\Contracts\Database\Query\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Partition extends BaseMetric
|
||||
{
|
||||
public function __construct(
|
||||
Model|Builder|string $model,
|
||||
public string $groupBy,
|
||||
MetricDateRange $dateRange,
|
||||
string|Expression|null $column = null,
|
||||
?string $dateColumn = null,
|
||||
int $limit = 50,
|
||||
protected array $additionalColumns = [],
|
||||
) {
|
||||
parent::__construct($model, $dateRange, $column, $dateColumn, $limit);
|
||||
}
|
||||
|
||||
protected function aggregate(string $function): array
|
||||
{
|
||||
$select = [
|
||||
$this->groupBy,
|
||||
DB::raw("{$function}({$this->getWrappedColumn()}) as aggregate"),
|
||||
...$this->additionalColumns,
|
||||
];
|
||||
|
||||
$results = $this->query
|
||||
->select($select)
|
||||
->groupBy($this->groupBy, ...$this->additionalColumns)
|
||||
->when(
|
||||
$this->dateRange,
|
||||
fn($query) => $query->whereBetween($this->dateColumn, [
|
||||
$this->dateRange->start,
|
||||
$this->dateRange->end,
|
||||
]),
|
||||
)
|
||||
->limit($this->limit)
|
||||
->get();
|
||||
|
||||
$data = $results->map(function ($result) {
|
||||
$finalResult = [
|
||||
'label' => $this->getLabel($result),
|
||||
'value' => $this->round($result->aggregate),
|
||||
];
|
||||
foreach ($this->additionalColumns as $column) {
|
||||
$finalResult[$column] = $result->{$column};
|
||||
}
|
||||
return $finalResult;
|
||||
});
|
||||
$total = $data->sum('value');
|
||||
$data = $data
|
||||
->map(function ($item) use ($total) {
|
||||
$item['percentage'] = round((100 * $item['value']) / $total, 1);
|
||||
return $item;
|
||||
})
|
||||
->sortByDesc('value')
|
||||
->values();
|
||||
|
||||
return $data->all();
|
||||
}
|
||||
|
||||
protected function getLabel(Model $result): string
|
||||
{
|
||||
$label = with(
|
||||
$result->{last(explode('.', $this->groupBy))},
|
||||
fn($key) => $key,
|
||||
);
|
||||
|
||||
return __(ucfirst($label));
|
||||
}
|
||||
}
|
||||
44
common/Database/Metrics/Traits/GeneratesTrendResults.php
Executable file
44
common/Database/Metrics/Traits/GeneratesTrendResults.php
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Metrics\Traits;
|
||||
|
||||
use Carbon\CarbonInterface;
|
||||
use Common\Database\Metrics\MetricDateRange;
|
||||
|
||||
trait GeneratesTrendResults
|
||||
{
|
||||
protected function getAllPossibleDateResults(
|
||||
MetricDateRange $dateRange,
|
||||
): array {
|
||||
$format = $dateRange->getGroupingFormat();
|
||||
|
||||
// dates in range will already be in correct timezone
|
||||
$possibleDateResults = [];
|
||||
foreach ($dateRange->period as $date) {
|
||||
$possibleDateResults[
|
||||
(string) $date->format($format)
|
||||
] = $this->formatTrendResult($dateRange->granularity, $date);
|
||||
}
|
||||
|
||||
return $possibleDateResults;
|
||||
}
|
||||
|
||||
protected function formatTrendResult(
|
||||
string $granularity,
|
||||
CarbonInterface $date,
|
||||
int $value = 0,
|
||||
): array {
|
||||
if ($granularity === $this->dateRange::WEEK) {
|
||||
return [
|
||||
'date' => $date->startOfWeek()->toISOString(),
|
||||
'endDate' => $date->endOfWeek()->toISOString(),
|
||||
'value' => $value,
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'date' => $date->toISOString(),
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
32
common/Database/Metrics/Traits/RoundingPrecision.php
Executable file
32
common/Database/Metrics/Traits/RoundingPrecision.php
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Metrics\Traits;
|
||||
|
||||
trait RoundingPrecision
|
||||
{
|
||||
public int $roundingPrecision = 0;
|
||||
public int $roundingMode = PHP_ROUND_HALF_UP;
|
||||
|
||||
/**
|
||||
* Set the precision level used when rounding the value.
|
||||
*/
|
||||
public function precision(
|
||||
int $precision = 0,
|
||||
int $mode = PHP_ROUND_HALF_UP,
|
||||
): static {
|
||||
$this->roundingPrecision = $precision;
|
||||
|
||||
if (
|
||||
in_array($mode, [
|
||||
PHP_ROUND_HALF_UP,
|
||||
PHP_ROUND_HALF_DOWN,
|
||||
PHP_ROUND_HALF_EVEN,
|
||||
PHP_ROUND_HALF_ODD,
|
||||
])
|
||||
) {
|
||||
$this->roundingMode = $mode;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
71
common/Database/Metrics/Trend.php
Executable file
71
common/Database/Metrics/Trend.php
Executable file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Metrics;
|
||||
|
||||
use Common\Database\Metrics\Traits\GeneratesTrendResults;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Trend extends BaseMetric
|
||||
{
|
||||
use GeneratesTrendResults;
|
||||
|
||||
protected function aggregate(string $function): array
|
||||
{
|
||||
$expression = new TrendDateExpression(
|
||||
$this->query,
|
||||
$this->dateColumn,
|
||||
$this->dateRange->granularity,
|
||||
$this->dateRange->timezone,
|
||||
);
|
||||
|
||||
$results = $this->query
|
||||
->select(
|
||||
DB::raw(
|
||||
"{$expression->getValue(
|
||||
DB::connection()->getQueryGrammar(),
|
||||
)} as date_result, {$function}({$this->getWrappedColumn()}) as aggregate",
|
||||
),
|
||||
)
|
||||
->whereBetween($this->dateColumn, [
|
||||
$this->dateRange->start,
|
||||
$this->dateRange->end,
|
||||
])
|
||||
->groupBy(DB::raw($expression))
|
||||
->orderBy('date_result')
|
||||
->limit($this->limit)
|
||||
->get();
|
||||
|
||||
$mergedResults = array_replace(
|
||||
$this->getAllPossibleDateResults($this->dateRange),
|
||||
$results
|
||||
->mapWithKeys(function ($result) {
|
||||
return [
|
||||
(string) $result->date_result => $this->formatTrendResult(
|
||||
$this->dateRange->granularity,
|
||||
$this->parseMysqlDate($result->date_result),
|
||||
$result->aggregate,
|
||||
),
|
||||
];
|
||||
})
|
||||
->all(),
|
||||
);
|
||||
|
||||
return array_values($mergedResults);
|
||||
}
|
||||
|
||||
protected function parseMysqlDate(string $mysqlDate): Carbon
|
||||
{
|
||||
// dates coming from mysql will be in user's timezone (due to + Interval x HOUR),
|
||||
// set user's timezone on carbon as well, so that "toIsoString" will return correct date.
|
||||
if ($this->dateRange->granularity === $this->dateRange::WEEK) {
|
||||
[$year, $week] = explode('-', $mysqlDate);
|
||||
return Carbon::today($this->dateRange->timezone)->setISODate(
|
||||
(int) $year,
|
||||
(int) $week,
|
||||
);
|
||||
} else {
|
||||
return Carbon::parse($mysqlDate, $this->dateRange->timezone);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
common/Database/Metrics/TrendDateExpression.php
Executable file
87
common/Database/Metrics/TrendDateExpression.php
Executable file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Metrics;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Grammar;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
|
||||
class TrendDateExpression extends Expression
|
||||
{
|
||||
public function __construct(
|
||||
protected Builder $query,
|
||||
protected string $column,
|
||||
protected string $unit,
|
||||
protected string $timezone,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getValue(Grammar $grammar): string
|
||||
{
|
||||
// dates in database are stored in UTC, and date_format will not return hour in many cases, so we need
|
||||
// to convert date_format result to specified timezone in mysql because it can use the full timestamp
|
||||
$offset = $this->offset();
|
||||
if ($offset > 0) {
|
||||
$interval = '+ INTERVAL ' . $offset . ' HOUR';
|
||||
} elseif ($offset === 0) {
|
||||
$interval = '';
|
||||
} else {
|
||||
$interval = '- INTERVAL ' . $offset * -1 . ' HOUR';
|
||||
}
|
||||
|
||||
switch ($this->unit) {
|
||||
case 'year':
|
||||
return "date_format({$this->wrap(
|
||||
$this->column,
|
||||
)} {$interval}, '%Y')";
|
||||
case 'month':
|
||||
return "date_format({$this->wrap(
|
||||
$this->column,
|
||||
)} {$interval}, '%Y-%m')";
|
||||
case 'week':
|
||||
return "date_format({$this->wrap(
|
||||
$this->column,
|
||||
)} {$interval}, '%x-%v')";
|
||||
case 'day':
|
||||
return "date_format({$this->wrap(
|
||||
$this->column,
|
||||
)} {$interval}, '%Y-%m-%d')";
|
||||
case 'hour':
|
||||
return "date_format({$this->wrap(
|
||||
$this->column,
|
||||
)} {$interval}, '%Y-%m-%d %H:00')";
|
||||
case 'minute':
|
||||
return "date_format({$this->wrap(
|
||||
$this->column,
|
||||
)} {$interval}, '%Y-%m-%d %H:%i:00')";
|
||||
}
|
||||
}
|
||||
|
||||
protected function wrap(string $value): string
|
||||
{
|
||||
return $this->query
|
||||
->getQuery()
|
||||
->getGrammar()
|
||||
->wrap($value);
|
||||
}
|
||||
|
||||
protected function offset(): int
|
||||
{
|
||||
$timezoneOffset = function ($timezone) {
|
||||
return (new DateTime(
|
||||
CarbonImmutable::now()->format('Y-m-d H:i:s'),
|
||||
new DateTimeZone($timezone),
|
||||
))->getOffset() /
|
||||
60 /
|
||||
60;
|
||||
};
|
||||
|
||||
$appOffset = $timezoneOffset(config('app.timezone'));
|
||||
$userOffset = $timezoneOffset($this->timezone);
|
||||
|
||||
return $userOffset - $appOffset;
|
||||
}
|
||||
}
|
||||
68
common/Database/Metrics/ValueMetric.php
Executable file
68
common/Database/Metrics/ValueMetric.php
Executable file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Metrics;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
|
||||
class ValueMetric extends BaseMetric
|
||||
{
|
||||
public function __construct(
|
||||
Model|Builder|string $model,
|
||||
MetricDateRange $dateRange,
|
||||
string|Expression|null $column = null,
|
||||
?string $dateColumn = null,
|
||||
) {
|
||||
parent::__construct($model, $dateRange, $column, $dateColumn);
|
||||
}
|
||||
|
||||
protected function aggregate(string $function): array
|
||||
{
|
||||
$column =
|
||||
$this->column ?: $this->query->getModel()->getQualifiedKeyName();
|
||||
|
||||
$previousValue = round(
|
||||
(clone $this->query)
|
||||
->when($this->dateRange, function ($query) {
|
||||
$previous = $this->dateRange->getPreviousPeriod();
|
||||
$query->whereBetween($this->dateColumn, [
|
||||
$previous->start,
|
||||
$previous->end,
|
||||
]);
|
||||
})
|
||||
->{$function}($column) ?? 0,
|
||||
$this->roundingPrecision,
|
||||
$this->roundingMode,
|
||||
);
|
||||
|
||||
$currentValue = round(
|
||||
(clone $this->query)
|
||||
->when(
|
||||
$this->dateRange,
|
||||
fn($query) => $query->whereBetween($this->dateColumn, [
|
||||
$this->dateRange->start,
|
||||
$this->dateRange->end,
|
||||
]),
|
||||
)
|
||||
->{$function}($column) ?? 0,
|
||||
$this->roundingPrecision,
|
||||
$this->roundingMode,
|
||||
);
|
||||
|
||||
if (!$currentValue && !$previousValue) {
|
||||
$percentageChange = 0; // no change
|
||||
} elseif (!$previousValue) {
|
||||
$percentageChange = 100; // 100% increase
|
||||
} else {
|
||||
$percentageChange =
|
||||
(($currentValue - $previousValue) / $previousValue) * 100;
|
||||
}
|
||||
|
||||
return [
|
||||
'previousValue' => $previousValue,
|
||||
'currentValue' => $currentValue,
|
||||
'percentageChange' => min(300, round($percentageChange, 2)),
|
||||
];
|
||||
}
|
||||
}
|
||||
58
common/Database/MigrateAndSeed.php
Executable file
58
common/Database/MigrateAndSeed.php
Executable file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database;
|
||||
|
||||
use Common\Admin\Appearance\GenerateFavicon;
|
||||
use Common\Core\Manifest\BuildManifestFile;
|
||||
use Database\Seeders\DatabaseSeeder;
|
||||
use File;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class MigrateAndSeed
|
||||
{
|
||||
public function execute(callable $afterMigrateCallback = null): void
|
||||
{
|
||||
// Migrate
|
||||
if (!app('migrator')->repositoryExists()) {
|
||||
app('migration.repository')->createRepository();
|
||||
}
|
||||
$migrator = app('migrator');
|
||||
$paths = $migrator->paths();
|
||||
$paths[] = app('path.database') . DIRECTORY_SEPARATOR . 'migrations';
|
||||
$migrator->run($paths);
|
||||
|
||||
$afterMigrateCallback && $afterMigrateCallback();
|
||||
|
||||
$this->runCommonSeeders();
|
||||
|
||||
// Seed
|
||||
$seeder = class_exists(\DatabaseSeeder::class)
|
||||
? app(\DatabaseSeeder::class)
|
||||
: app(DatabaseSeeder::class);
|
||||
$seeder->setContainer(app());
|
||||
Model::unguarded(function () use ($seeder) {
|
||||
$seeder->__invoke();
|
||||
});
|
||||
|
||||
// Manifest
|
||||
app(BuildManifestFile::class)->execute();
|
||||
|
||||
$defaultFaviconPath = public_path('images/favicon-original.png');
|
||||
if (!env('INSTALLED') && file_exists($defaultFaviconPath)) {
|
||||
app(GenerateFavicon::class)->execute($defaultFaviconPath);
|
||||
}
|
||||
}
|
||||
|
||||
public function runCommonSeeders(): void
|
||||
{
|
||||
$paths = File::files(app('path.common') . '/Database/Seeds');
|
||||
foreach ($paths as $fileInfo) {
|
||||
Model::unguarded(function () use ($fileInfo) {
|
||||
$namespace =
|
||||
'Common\Database\Seeds\\' . $fileInfo->getBaseName('.php');
|
||||
$seeder = app($namespace)->setContainer(app());
|
||||
$seeder->__invoke();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
324
common/Database/Paginator.php
Executable file
324
common/Database/Paginator.php
Executable file
@@ -0,0 +1,324 @@
|
||||
<?php namespace Common\Database;
|
||||
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
use Closure;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Paginator
|
||||
{
|
||||
/**
|
||||
* @var Builder
|
||||
*/
|
||||
private $query;
|
||||
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
private $model;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $defaultOrderColumn = 'updated_at';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $defaultOrderDirection = 'desc';
|
||||
|
||||
/**
|
||||
* @var Closure
|
||||
*/
|
||||
public $secondaryOrderCallback;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $defaultPerPage = 15;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $searchColumn = 'name';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $filterColumns = [];
|
||||
|
||||
/**
|
||||
* @var Closure
|
||||
*/
|
||||
public $searchCallback;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $params;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $dontSort;
|
||||
|
||||
public function __construct($model, array $params)
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->params = $this->toCamelCase($params);
|
||||
$this->query = $model->newQuery();
|
||||
}
|
||||
|
||||
public function paginate(): LengthAwarePaginator
|
||||
{
|
||||
$with = array_filter(explode(',', $this->param('with', '')));
|
||||
$withCount = array_filter(explode(',', $this->param('withCount', '')));
|
||||
$searchTerm = $this->param('query');
|
||||
$order = $this->getOrder();
|
||||
$perPage = $this->param('perPage', $this->defaultPerPage);
|
||||
$page = (int) $this->param('page', 1);
|
||||
|
||||
// load specified relations and counts
|
||||
if (!empty($with)) {
|
||||
$this->query->with($with);
|
||||
}
|
||||
if (!empty($withCount)) {
|
||||
$this->query->withCount($withCount);
|
||||
}
|
||||
|
||||
// search
|
||||
if ($searchTerm) {
|
||||
if ($this->searchCallback) {
|
||||
call_user_func(
|
||||
$this->searchCallback,
|
||||
$this->query,
|
||||
$searchTerm,
|
||||
);
|
||||
} else {
|
||||
$this->query->where(
|
||||
$this->searchColumn,
|
||||
'like',
|
||||
"$searchTerm%",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->applyFilters();
|
||||
|
||||
// order
|
||||
if (!$this->dontSort) {
|
||||
if ($this->isRawOrder($order['col'])) {
|
||||
$this->query->orderByRaw($order['col']);
|
||||
} else {
|
||||
$this->query->orderBy($order['col'], $order['dir']);
|
||||
}
|
||||
|
||||
if ($this->secondaryOrderCallback) {
|
||||
call_user_func(
|
||||
$this->secondaryOrderCallback,
|
||||
$this->query,
|
||||
$order['col'],
|
||||
$order['dir'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$countCacheKeyQuery = $this->query
|
||||
->toBase()
|
||||
->cloneWithout(['columns', 'orders', 'limit', 'offset']);
|
||||
$countCacheKye = base64_encode(
|
||||
Str::replaceArray(
|
||||
'?',
|
||||
$countCacheKeyQuery->getBindings(),
|
||||
$countCacheKeyQuery->toSql(),
|
||||
),
|
||||
);
|
||||
$total = null;
|
||||
if ($countCacheKye) {
|
||||
$total = Cache::get($countCacheKye);
|
||||
}
|
||||
if (is_null($total)) {
|
||||
$total = $this->query->toBase()->getCountForPagination();
|
||||
if ($total > 500000) {
|
||||
Cache::put($countCacheKye, $total, Carbon::now()->addDay());
|
||||
}
|
||||
}
|
||||
|
||||
$items = $total
|
||||
? $this->query->forPage($page, $perPage)->get()
|
||||
: new Collection();
|
||||
|
||||
return Container::getInstance()->makeWith(LengthAwarePaginator::class, [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'perPage' => $perPage,
|
||||
'page' => $page,
|
||||
]);
|
||||
}
|
||||
|
||||
public function param(string $name, $default = null)
|
||||
{
|
||||
return Arr::get($this->params, Str::camel($name)) ?: $default;
|
||||
}
|
||||
|
||||
public function setParam(string $name, $value): self
|
||||
{
|
||||
$this->params[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load specified relation counts with paginator items.
|
||||
*
|
||||
* @param mixed $relations
|
||||
* @return $this
|
||||
*/
|
||||
public function withCount($relations)
|
||||
{
|
||||
$this->query->withCount($relations);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load specified relations of paginated items.
|
||||
*
|
||||
* @param mixed $relations
|
||||
* @return $this
|
||||
*/
|
||||
public function with($relations)
|
||||
{
|
||||
$this->query->with($relations);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $column
|
||||
* @param null $operator
|
||||
* @param null $value
|
||||
* @param string $boolean
|
||||
* @return $this
|
||||
*/
|
||||
public function where(
|
||||
$column,
|
||||
$operator = null,
|
||||
$value = null,
|
||||
$boolean = 'and'
|
||||
) {
|
||||
$this->query->where($column, $operator, $value, $boolean);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDefaultOrderColumns($column, $direction = 'desc'): self
|
||||
{
|
||||
$this->defaultOrderColumn = $column;
|
||||
$this->defaultOrderDirection = $direction;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getOrder()
|
||||
{
|
||||
// order provided as single string: "column|direction"
|
||||
if ($specifiedOrder = $this->param('order')) {
|
||||
$parts = preg_split('(\||:)', $specifiedOrder);
|
||||
$orderCol = Arr::get($parts, 0, $this->defaultOrderColumn);
|
||||
$orderDir = Arr::get($parts, 1, $this->defaultOrderDirection);
|
||||
} else {
|
||||
$orderCol = $this->param('orderBy', $this->defaultOrderColumn);
|
||||
$orderDir = $this->param('orderDir', $this->defaultOrderDirection);
|
||||
}
|
||||
|
||||
return [
|
||||
'dir' => Str::snake($orderDir),
|
||||
'col' => !$this->isRawOrder($orderCol)
|
||||
? Str::snake($orderCol)
|
||||
: $orderCol,
|
||||
];
|
||||
}
|
||||
|
||||
private function toCamelCase($params)
|
||||
{
|
||||
return collect($params)
|
||||
->keyBy(function ($value, $key) {
|
||||
return Str::camel($key);
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private function applyFilters()
|
||||
{
|
||||
foreach ($this->filterColumns as $column => $callback) {
|
||||
$column = is_int($column) ? $callback : $column;
|
||||
$column = Str::camel($column);
|
||||
if (isset($this->params[$column])) {
|
||||
$value = $this->params[$column];
|
||||
$column = Str::snake($column);
|
||||
|
||||
// user specified callback
|
||||
if (is_callable($callback)) {
|
||||
$callback($this->query, $value);
|
||||
|
||||
// boolean filter
|
||||
} elseif ($value === 'false' || $value === 'true') {
|
||||
$this->applyBooleanFilter($column, $value);
|
||||
|
||||
// filter by between date
|
||||
} elseif (
|
||||
\Str::contains($column, '_at') &&
|
||||
\Str::contains($value, ':')
|
||||
) {
|
||||
$this->query()->whereBetween($column, explode(':', $value));
|
||||
|
||||
// filter by specified column value
|
||||
} else {
|
||||
$this->query()->where($column, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $column
|
||||
* @param string $value
|
||||
*/
|
||||
private function applyBooleanFilter($column, $value)
|
||||
{
|
||||
// cast "true" or "false" to boolean
|
||||
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
$casts = $this->model->getCasts();
|
||||
|
||||
// column is a simple boolean type
|
||||
if (Arr::get($casts, $column) === 'boolean') {
|
||||
$this->query()->where($column, $value);
|
||||
|
||||
// column has actual value, test whether it's null or not by default
|
||||
} else {
|
||||
if ($value) {
|
||||
$this->query()->whereNotNull($column);
|
||||
} else {
|
||||
$this->query()->whereNull($column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isRawOrder(string $order): bool
|
||||
{
|
||||
return Str::contains($order, ['(', ')']);
|
||||
}
|
||||
}
|
||||
50
common/Database/Seeds/CssThemesTableSeeder.php
Executable file
50
common/Database/Seeds/CssThemesTableSeeder.php
Executable file
@@ -0,0 +1,50 @@
|
||||
<?php namespace Common\Database\Seeds;
|
||||
|
||||
use App\Models\User;
|
||||
use Common\Admin\Appearance\Themes\CssTheme;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class CssThemesTableSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$dark = config('common.themes.dark');
|
||||
$light = config('common.themes.light');
|
||||
|
||||
$admin = User::whereHas('permissions', function (Builder $builder) {
|
||||
$builder->where('name', 'admin');
|
||||
})->first();
|
||||
|
||||
$darkTheme = CssTheme::where('default_dark', true)
|
||||
->orWhere('name', 'Dark')
|
||||
->first();
|
||||
if (!$darkTheme || !$darkTheme->getRawOriginal('values')) {
|
||||
if ($darkTheme) {
|
||||
$darkTheme->delete();
|
||||
}
|
||||
CssTheme::create([
|
||||
'name' => 'Dark',
|
||||
'is_dark' => true,
|
||||
'default_dark' => true,
|
||||
'values' => $dark,
|
||||
'user_id' => $admin ? $admin->id : 1,
|
||||
]);
|
||||
}
|
||||
|
||||
$lightTheme = CssTheme::where('default_light', true)
|
||||
->orWhere('name', 'Light')
|
||||
->first();
|
||||
if (!$lightTheme || !$lightTheme->getRawOriginal('values')) {
|
||||
if ($lightTheme) {
|
||||
$lightTheme->delete();
|
||||
}
|
||||
CssTheme::create([
|
||||
'name' => 'Light',
|
||||
'default_light' => true,
|
||||
'user_id' => $admin ? $admin->id : 1,
|
||||
'values' => $light,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
79
common/Database/Seeds/DefaultPagesSeeder.php
Executable file
79
common/Database/Seeds/DefaultPagesSeeder.php
Executable file
@@ -0,0 +1,79 @@
|
||||
<?php namespace Common\Database\Seeds;
|
||||
|
||||
use Common\Pages\CustomPage;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DefaultPagesSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
CustomPage::firstOrCreate(
|
||||
[
|
||||
'slug' => 'privacy-policy',
|
||||
],
|
||||
[
|
||||
'title' => 'Example Privacy Policy',
|
||||
'slug' => 'privacy-policy',
|
||||
'body' => $this->replacePlaceholders(
|
||||
file_get_contents(
|
||||
base_path(
|
||||
'common/resources/defaults/privacy-policy.html',
|
||||
),
|
||||
),
|
||||
),
|
||||
'type' => 'default',
|
||||
],
|
||||
);
|
||||
|
||||
CustomPage::firstOrCreate(
|
||||
[
|
||||
'slug' => 'terms-of-service',
|
||||
],
|
||||
[
|
||||
'title' => 'Example Terms of Service',
|
||||
'slug' => 'terms-of-service',
|
||||
'body' => $this->replacePlaceholders(
|
||||
file_get_contents(
|
||||
base_path(
|
||||
'common/resources/defaults/terms-of-service.html',
|
||||
),
|
||||
),
|
||||
),
|
||||
'type' => 'default',
|
||||
],
|
||||
);
|
||||
|
||||
CustomPage::firstOrCreate(
|
||||
[
|
||||
'slug' => 'about-us',
|
||||
],
|
||||
[
|
||||
'title' => 'Example About Us',
|
||||
'slug' => 'about-us',
|
||||
'body' => file_get_contents(
|
||||
base_path('common/resources/lorem.html'),
|
||||
),
|
||||
'type' => 'default',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
protected function replacePlaceholders(string $text): string
|
||||
{
|
||||
return str_replace(
|
||||
[
|
||||
'[Website Name]',
|
||||
'[Website URL]',
|
||||
'[Contact Email]',
|
||||
'[Your Country/State]',
|
||||
],
|
||||
[
|
||||
config('app.name'),
|
||||
url('/'),
|
||||
settings('mail.contact_page_address'),
|
||||
'United States',
|
||||
],
|
||||
$text,
|
||||
);
|
||||
}
|
||||
}
|
||||
45
common/Database/Seeds/LocalizationsTableSeeder.php
Executable file
45
common/Database/Seeds/LocalizationsTableSeeder.php
Executable file
@@ -0,0 +1,45 @@
|
||||
<?php namespace Common\Database\Seeds;
|
||||
|
||||
use Common\Localizations\Localization;
|
||||
use Common\Localizations\LocalizationsRepository;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class LocalizationsTableSeeder extends Seeder
|
||||
{
|
||||
public function __construct(protected LocalizationsRepository $repository)
|
||||
{
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$localizations = Localization::all();
|
||||
|
||||
if ($localizations->isNotEmpty()) {
|
||||
$this->mergeExistingTranslationLines($localizations);
|
||||
} else {
|
||||
$this->repository->create([
|
||||
'name' => 'English',
|
||||
'language' => 'en',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge existing localization translation lines with default ones.
|
||||
*/
|
||||
private function mergeExistingTranslationLines(Collection $localizations)
|
||||
{
|
||||
$defaultLines = $this->repository->getDefaultTranslationLines();
|
||||
|
||||
$localizations->each(function ($localization) use ($defaultLines) {
|
||||
$this->repository->storeLocalizationLines(
|
||||
$localization,
|
||||
array_merge(
|
||||
$defaultLines,
|
||||
$this->repository->getLocalizationLines($localization),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
14
common/Database/Seeds/MailTemplatesSeeder.php
Executable file
14
common/Database/Seeds/MailTemplatesSeeder.php
Executable file
@@ -0,0 +1,14 @@
|
||||
<?php namespace Common\Database\Seeds;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class MailTemplatesSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
29
common/Database/Seeds/PermissionTableSeeder.php
Executable file
29
common/Database/Seeds/PermissionTableSeeder.php
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php namespace Common\Database\Seeds;
|
||||
|
||||
use Common\Auth\Permissions\Permission;
|
||||
use Common\Core\Values\GetStaticPermissions;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class PermissionTableSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$allPermissions = app(GetStaticPermissions::class)->execute();
|
||||
$allPermissions['admin'][] = [
|
||||
'name' => 'admin',
|
||||
'display_name' => 'Super Admin',
|
||||
'description' => 'Give all permissions to user.',
|
||||
'group' => 'admin',
|
||||
];
|
||||
|
||||
foreach ($allPermissions as $groupName => $group) {
|
||||
foreach ($group as $permission) {
|
||||
$permission['group'] = $groupName;
|
||||
app(Permission::class)->updateOrCreate(['name' => $permission['name']], $permission);
|
||||
}
|
||||
}
|
||||
|
||||
// delete legacy permissions
|
||||
app(Permission::class)->whereNull('group')->delete();
|
||||
}
|
||||
}
|
||||
128
common/Database/Seeds/RolesTableSeeder.php
Executable file
128
common/Database/Seeds/RolesTableSeeder.php
Executable file
@@ -0,0 +1,128 @@
|
||||
<?php namespace Common\Database\Seeds;
|
||||
|
||||
use App\Models\User;
|
||||
use Common\Auth\Permissions\Permission;
|
||||
use Common\Auth\Permissions\Traits\SyncsPermissions;
|
||||
use Common\Auth\Roles\Role;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RolesTableSeeder extends Seeder
|
||||
{
|
||||
use SyncsPermissions;
|
||||
|
||||
private array $commonConfig = [];
|
||||
private array $appConfig = [];
|
||||
|
||||
public function __construct(
|
||||
protected Role $role,
|
||||
protected User $user,
|
||||
protected Permission $permission,
|
||||
protected Filesystem $fs,
|
||||
) {
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$this->commonConfig = File::getRequire(
|
||||
app('path.common') . '/resources/defaults/permissions.php',
|
||||
);
|
||||
$this->appConfig = File::getRequire(
|
||||
resource_path('defaults/permissions.php'),
|
||||
);
|
||||
|
||||
foreach ($this->appConfig['roles'] as $appRole) {
|
||||
if ($commonRoleName = Arr::get($appRole, 'extends')) {
|
||||
$commonRole = $this->findRoleConfig($commonRoleName);
|
||||
$appRole = array_merge($commonRole, $appRole);
|
||||
$appRole['permissions'] = array_merge(
|
||||
$commonRole['permissions'],
|
||||
$appRole['permissions'],
|
||||
);
|
||||
}
|
||||
|
||||
// skip billing permissions if billing is not integrated
|
||||
$appRole['permissions'] = array_filter(
|
||||
$appRole['permissions'],
|
||||
function ($permission) {
|
||||
if (is_array($permission)) {
|
||||
$permission = $permission['name'];
|
||||
}
|
||||
return config('common.site.billing_integrated') ||
|
||||
!Str::contains($permission, ['invoice.', 'plans.']);
|
||||
},
|
||||
);
|
||||
|
||||
$this->createOrUpdateRole($appRole);
|
||||
}
|
||||
}
|
||||
|
||||
private function findRoleConfig(string $roleName): array
|
||||
{
|
||||
$roleConfig = Arr::first($this->commonConfig['roles'], function (
|
||||
$role,
|
||||
) use ($roleName) {
|
||||
return $role['name'] === $roleName;
|
||||
});
|
||||
if (!$roleConfig) {
|
||||
$roleConfig = Arr::first($this->appConfig['roles'], function (
|
||||
$role,
|
||||
) use ($roleName) {
|
||||
return $role['name'] === $roleName;
|
||||
});
|
||||
}
|
||||
return $roleConfig;
|
||||
}
|
||||
|
||||
private function createOrUpdateRole(array $appRole): Role
|
||||
{
|
||||
$defaultPermissions = collect($appRole['permissions'])->map(
|
||||
fn($permission) => is_string($permission)
|
||||
? ['name' => $permission]
|
||||
: $permission,
|
||||
);
|
||||
|
||||
$dbPermissions = Permission::whereIn(
|
||||
'name',
|
||||
$defaultPermissions->pluck('name'),
|
||||
)->get();
|
||||
$dbPermissions->map(function (Permission $permission) use (
|
||||
$defaultPermissions,
|
||||
) {
|
||||
$defaultPermission = $defaultPermissions
|
||||
->where('name', $permission['name'])
|
||||
->first();
|
||||
$permission['restrictions'] =
|
||||
Arr::get($defaultPermission, 'restrictions') ?: [];
|
||||
return $permission;
|
||||
});
|
||||
|
||||
if (Arr::get($appRole, 'default')) {
|
||||
$attributes = ['default' => true];
|
||||
Role::where('name', $appRole['name'])->update(['default' => true]);
|
||||
} elseif (Arr::get($appRole, 'guests')) {
|
||||
$attributes = ['guests' => true];
|
||||
Role::where('name', $appRole['name'])->update(['guests' => true]);
|
||||
} else {
|
||||
$attributes = ['name' => $appRole['name']];
|
||||
}
|
||||
|
||||
if ($role = Role::where($attributes)->first()) {
|
||||
return $role;
|
||||
} else {
|
||||
$role = $this->role->create(
|
||||
Arr::except($appRole, ['permissions', 'extends']),
|
||||
);
|
||||
$this->syncPermissions(
|
||||
$role,
|
||||
$role->permissions->concat($dbPermissions),
|
||||
);
|
||||
$role->save();
|
||||
|
||||
return $role;
|
||||
}
|
||||
}
|
||||
}
|
||||
109
common/Database/Seeds/SettingsTableSeeder.php
Executable file
109
common/Database/Seeds/SettingsTableSeeder.php
Executable file
@@ -0,0 +1,109 @@
|
||||
<?php namespace Common\Database\Seeds;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Common\Settings\Setting;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SettingsTableSeeder extends Seeder
|
||||
{
|
||||
public function __construct(protected Setting $setting)
|
||||
{
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$defaultSettings = config('common.default-settings');
|
||||
|
||||
$names = [];
|
||||
|
||||
$defaultSettings = array_map(function ($setting) use (&$names) {
|
||||
$names[] = $setting['name'];
|
||||
|
||||
$setting['created_at'] = Carbon::now();
|
||||
$setting['updated_at'] = Carbon::now();
|
||||
|
||||
//make sure all settings have "private" field to
|
||||
//avoid db errors due to different column count
|
||||
if (!array_key_exists('private', $setting)) {
|
||||
$setting['private'] = 0;
|
||||
}
|
||||
|
||||
// cast booleans to string as "insert"
|
||||
// method will not use Setting model setters
|
||||
if ($setting['value'] === true) {
|
||||
$setting['value'] = 'true';
|
||||
} elseif ($setting['value'] === false) {
|
||||
$setting['value'] = 'false';
|
||||
}
|
||||
$setting['value'] = (string) $setting['value'];
|
||||
|
||||
// add ids to menus and menu items, if don't have one already
|
||||
if ($setting['name'] === 'menus') {
|
||||
$value = json_decode($setting['value'], true);
|
||||
foreach ($value as &$menu) {
|
||||
if (!isset($menu['id'])) {
|
||||
$menu['id'] = Str::random(6);
|
||||
}
|
||||
foreach ($menu['items'] as &$item) {
|
||||
if (!isset($item['id'])) {
|
||||
$item['id'] = Str::random(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
$setting['value'] = json_encode($value);
|
||||
}
|
||||
|
||||
return $setting;
|
||||
}, $defaultSettings);
|
||||
|
||||
$existing = $this->setting->whereIn('name', $names)->pluck('name');
|
||||
|
||||
//only insert settings that don't already exist in database
|
||||
$new = array_filter($defaultSettings, function ($setting) use (
|
||||
$existing,
|
||||
) {
|
||||
return !$existing->contains($setting['name']);
|
||||
});
|
||||
|
||||
$this->setting->insert($new);
|
||||
|
||||
$this->mergeMenusSetting($defaultSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge existing menus setting json with new one.
|
||||
*/
|
||||
private function mergeMenusSetting(array $defaultSettings): void
|
||||
{
|
||||
$existing =
|
||||
$this->setting->where('name', 'menus')->first()->value ?? [];
|
||||
$new = json_decode(
|
||||
Arr::first(
|
||||
$defaultSettings,
|
||||
fn($value) => $value['name'] === 'menus',
|
||||
)['value'],
|
||||
true,
|
||||
);
|
||||
|
||||
foreach ($new as $newMenu) {
|
||||
$alreadyHas = Arr::first(
|
||||
$existing,
|
||||
fn($value) => $value['name'] === $newMenu['name'],
|
||||
);
|
||||
|
||||
foreach ($newMenu['items'] as $index => $item) {
|
||||
$newMenu['items'][$index]['order'] = $index;
|
||||
}
|
||||
|
||||
if (!$alreadyHas) {
|
||||
$existing[] = $newMenu;
|
||||
}
|
||||
}
|
||||
|
||||
$this->setting
|
||||
->where('name', 'menus')
|
||||
->update(['value' => json_encode($existing)]);
|
||||
}
|
||||
}
|
||||
20
common/Database/Traits/AddsIndexToExistingTable.php
Executable file
20
common/Database/Traits/AddsIndexToExistingTable.php
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Database\Traits;
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
trait AddsIndexToExistingTable
|
||||
{
|
||||
protected function addIndexIfDoesNotExist(Blueprint $table, string $column) {
|
||||
$prefix = Schema::getConnection()->getTablePrefix();
|
||||
$sm = Schema::getConnection()->getDoctrineSchemaManager();
|
||||
$tableName = "{$prefix}{$table->getTable()}";
|
||||
$indexesFound = $sm->listTableIndexes($tableName);
|
||||
if (!array_key_exists("{$tableName}_{$column}_index", $indexesFound)) {
|
||||
$table->index($column);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
55
common/Database/migrations/2014_10_12_000000_create_users_table.php
Executable file
55
common/Database/migrations/2014_10_12_000000_create_users_table.php
Executable file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('users')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('username', 100)->nullable();
|
||||
$table->string('first_name', 100)->nullable();
|
||||
$table->string('last_name', 100)->nullable();
|
||||
$table->string('avatar_url')->nullable();
|
||||
$table->string('gender', 20)->nullable();
|
||||
$table->text('permissions')->nullable();
|
||||
$table->string('email')->unique();
|
||||
$table->string('password', 60);
|
||||
$table->string('card_brand', 30)->nullable();
|
||||
$table->string('card_last_four', 4)->nullable();
|
||||
$table->rememberToken();
|
||||
$table
|
||||
->timestamp('created_at')
|
||||
->index()
|
||||
->nullable();
|
||||
$table
|
||||
->timestamp('updated_at')
|
||||
->index()
|
||||
->nullable();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('users');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePasswordResetsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('password_resets')) return;
|
||||
|
||||
Schema::create('password_resets', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('email')->index();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('password_resets');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateSocialProfilesTable extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('social_profiles')) return;
|
||||
|
||||
Schema::create('social_profiles', function(Blueprint $table)
|
||||
{
|
||||
$table->increments('id');
|
||||
$table->integer('user_id');
|
||||
$table->string('service_name', 20);
|
||||
$table->string('user_service_id');
|
||||
$table->string('username')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('user_id');
|
||||
$table->unique(['user_id', 'service_name']);
|
||||
$table->unique(['service_name', 'user_service_id']);
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('social_profiles');
|
||||
}
|
||||
|
||||
}
|
||||
39
common/Database/migrations/2015_05_29_131549_create_settings_table.php
Executable file
39
common/Database/migrations/2015_05_29_131549_create_settings_table.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateSettingsTable extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('settings')) return;
|
||||
|
||||
Schema::create('settings', function(Blueprint $table)
|
||||
{
|
||||
$table->increments('id');
|
||||
$table->string('name')->unique();
|
||||
$table->text('value');
|
||||
$table->timestamps();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('settings');
|
||||
}
|
||||
|
||||
}
|
||||
41
common/Database/migrations/2015_10_23_164355_create_follows_table.php
Executable file
41
common/Database/migrations/2015_10_23_164355_create_follows_table.php
Executable file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class CreateFollowsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('follows')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::create('follows', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('follower_id');
|
||||
$table->integer('followed_id');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['follower_id', 'followed_id']);
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('follows');
|
||||
}
|
||||
}
|
||||
39
common/Database/migrations/2016_05_12_190852_create_tags_table.php
Executable file
39
common/Database/migrations/2016_05_12_190852_create_tags_table.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateTagsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('tags')) return;
|
||||
|
||||
Schema::create('tags', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->index()->unique();
|
||||
$table->string('display_name')->nullable();
|
||||
$table->string('type', 30)->index()->default('custom');
|
||||
$table->timestamp('created_at')->index()->nullable();
|
||||
$table->timestamp('updated_at')->index()->nullable();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('tags');
|
||||
}
|
||||
}
|
||||
38
common/Database/migrations/2016_05_12_190958_create_taggables_table.php
Executable file
38
common/Database/migrations/2016_05_12_190958_create_taggables_table.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class CreateTaggablesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('taggables', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('tag_id')->unsigned()->index();
|
||||
$table->integer('taggable_id')->unsigned()->index();
|
||||
$table->string('taggable_type', 80)->index();
|
||||
$table->integer('user_id')->unsigned()->index()->nullable();
|
||||
|
||||
$table->unique(['tag_id', 'taggable_id', 'user_id', 'taggable_type'], 'taggable_unique');
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('taggables');
|
||||
}
|
||||
}
|
||||
31
common/Database/migrations/2016_05_26_170044_create_uploads_table.php
Executable file
31
common/Database/migrations/2016_05_26_170044_create_uploads_table.php
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class CreateUploadsTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('uploads', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->index();
|
||||
$table->string('file_name', 36)->unique();
|
||||
$table->string('file_size');
|
||||
$table->string('mime');
|
||||
$table->string('extension');
|
||||
$table->string('user_id')->index();
|
||||
$table->string('url')->nullable();
|
||||
$table->string('thumbnail_url')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('uploads');
|
||||
}
|
||||
}
|
||||
34
common/Database/migrations/2016_05_27_143158_create_uploadables_table.php
Executable file
34
common/Database/migrations/2016_05_27_143158_create_uploadables_table.php
Executable file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class CreateUploadablesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('uploadables', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('upload_id');
|
||||
$table->unsignedInteger('uploadable_id');
|
||||
$table->string('uploadable_type', 60);
|
||||
|
||||
$table->unique(['upload_id', 'uploadable_id', 'uploadable_type'], 'uploadable_unique');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('uploadables');
|
||||
}
|
||||
}
|
||||
39
common/Database/migrations/2016_07_14_153703_create_groups_table.php
Executable file
39
common/Database/migrations/2016_07_14_153703_create_groups_table.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateGroupsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('groups')) return;
|
||||
|
||||
Schema::create('groups', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->unique();
|
||||
$table->text('permissions')->nullable();
|
||||
$table->boolean('default')->default(0)->unsigned()->index();
|
||||
$table->boolean('guests')->default(0)->unsigned()->index();
|
||||
$table->timestamps();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('groups');
|
||||
}
|
||||
}
|
||||
37
common/Database/migrations/2016_07_14_153921_create_user_group_table.php
Executable file
37
common/Database/migrations/2016_07_14_153921_create_user_group_table.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateUserGroupTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('user_group', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('user_id');
|
||||
$table->integer('group_id');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
|
||||
$table->unique(['user_id', 'group_id']);
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('user_group');
|
||||
}
|
||||
}
|
||||
38
common/Database/migrations/2017_07_02_120142_create_pages_table.php
Executable file
38
common/Database/migrations/2017_07_02_120142_create_pages_table.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('pages')) return;
|
||||
|
||||
Schema::create('pages', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->longText('body');
|
||||
$table->string('slug')->unique()->index();
|
||||
$table->timestamps();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('pages');
|
||||
}
|
||||
}
|
||||
35
common/Database/migrations/2017_07_11_122825_create_localizations_table.php
Executable file
35
common/Database/migrations/2017_07_11_122825_create_localizations_table.php
Executable file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateLocalizationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('localizations', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->index();
|
||||
$table->timestamps();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('localizations');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPrivateFieldToSettingsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasColumn('settings', 'private')) return;
|
||||
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->boolean('private')->default(0)->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->dropColumn('private');
|
||||
});
|
||||
}
|
||||
}
|
||||
28
common/Database/migrations/2017_09_17_144728_add_columns_to_users_table.php
Executable file
28
common/Database/migrations/2017_09_17_144728_add_columns_to_users_table.php
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddColumnsToUsersTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasColumn('users', 'language')) return;
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('language', 6)->nullable();
|
||||
$table->string('country', 40)->nullable();
|
||||
$table->string('timezone', 30)->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('language');
|
||||
$table->dropColumn('country');
|
||||
$table->dropColumn('timezone');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class MakePasswordColumnNullable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('password', 60)->nullable()->change();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('password', 60)->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class MakeSettingsValueColumnNullable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->text('value')->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('settings', function (Blueprint $table) {
|
||||
$table->text('value')->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPublicColumnToUploadsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('uploads', function (Blueprint $table) {
|
||||
$table->boolean('public')->default(0)->index();
|
||||
|
||||
// set collation to latin1_bin for maximum encoded string length
|
||||
$pathColumn = $table->string('path')->nullable();
|
||||
$pathColumn->collation = 'latin1_bin';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('uploads', function (Blueprint $table) {
|
||||
$table->dropColumn('public');
|
||||
$table->dropColumn('path');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddAvatarColumnToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasColumn('users', 'avatar')) return;
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('avatar')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->removeColumn('avatar');
|
||||
});
|
||||
}
|
||||
}
|
||||
39
common/Database/migrations/2018_01_10_140732_create_subscriptions_table.php
Executable file
39
common/Database/migrations/2018_01_10_140732_create_subscriptions_table.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSubscriptionsTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('subscriptions', function ($table) {
|
||||
$table->increments('id');
|
||||
$table->integer('user_id')->index();
|
||||
$table->string('plan_id')->index();
|
||||
$table
|
||||
->string('gateway')
|
||||
->default('none')
|
||||
->index();
|
||||
$table
|
||||
->string('gateway_id')
|
||||
->nullable()
|
||||
->unique()
|
||||
->index();
|
||||
$table->integer('quantity')->default(1);
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamp('trial_ends_at')->nullable();
|
||||
$table->timestamp('ends_at')->nullable();
|
||||
$table->timestamp('renews_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('subscriptions');
|
||||
}
|
||||
}
|
||||
34
common/Database/migrations/2018_01_10_140746_add_billing_to_users_table.php
Executable file
34
common/Database/migrations/2018_01_10_140746_add_billing_to_users_table.php
Executable file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddBillingToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasColumn('users', 'stripe_id')) return;
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('stripe_id')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('stripe_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
48
common/Database/migrations/2018_01_10_161706_create_billing_plans_table.php
Executable file
48
common/Database/migrations/2018_01_10_161706_create_billing_plans_table.php
Executable file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateBillingPlansTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('billing_plans', function(Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name');
|
||||
$table->integer('amount')->nullable();
|
||||
$table->string('currency');
|
||||
$table->string('currency_symbol')->default('$');
|
||||
$table->string('interval')->default('month');
|
||||
$table->integer('interval_count')->default(1);
|
||||
$table->integer('parent_id')->nullable();
|
||||
$table->text('permissions')->nullable();
|
||||
$table->uuid('uuid');
|
||||
$table->boolean('recommended')->default(0);
|
||||
$table->boolean('free')->default(0);
|
||||
$table->boolean('show_permissions')->default(0);
|
||||
$table->text('features')->nullable();
|
||||
$table->integer('position')->default(0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->collation = config('database.connections.mysql.collation');
|
||||
$table->charset = config('database.connections.mysql.charset');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('billing_plans');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddAvailableSpaceToBillingPlansTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('billing_plans', function (Blueprint $table) {
|
||||
$table->bigInteger('available_space')->nullable()->unsigned();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('billing_plans', function (Blueprint $table) {
|
||||
$table->dropColumn('available_space');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddAvailableSpaceToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasColumn('users', 'available_space')) return;
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->bigInteger('available_space')->nullable()->unsigned();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
if ( ! Schema::hasColumn('users', 'available_space')) return;
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('available_space');
|
||||
});
|
||||
}
|
||||
}
|
||||
44
common/Database/migrations/2018_07_26_142339_rename_groups_to_roles.php
Executable file
44
common/Database/migrations/2018_07_26_142339_rename_groups_to_roles.php
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RenameGroupsToRoles extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( ! Schema::hasTable('roles')) {
|
||||
Schema::table('groups', function (Blueprint $table) {
|
||||
$table->rename('roles');
|
||||
});
|
||||
}
|
||||
|
||||
if ( ! Schema::hasTable('user_role')) {
|
||||
Schema::table('user_group', function (Blueprint $table) {
|
||||
$table->rename('user_role');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('roles', function (Blueprint $table) {
|
||||
$table->rename('groups');
|
||||
});
|
||||
|
||||
Schema::table('user_role', function (Blueprint $table) {
|
||||
$table->rename('user_group');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RenameUserRoleTableColumnsToRoles extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('user_role', function (Blueprint $table) {
|
||||
$table->renameColumn('group_id', 'role_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('user_role', function (Blueprint $table) {
|
||||
$table->renameColumn('role_id', 'group_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RenameUploadsToFileEntries extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('file_entries')) return;
|
||||
|
||||
Schema::table('uploads', function (Blueprint $table) {
|
||||
$table->rename('file_entries');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->rename('uploads');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RefactorFileEntriesColumns extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
// refactoring was already done via another (app specific) migration
|
||||
if (Schema::hasColumn('file_entries', 'public_path')) return;
|
||||
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->bigInteger('file_size')->unsigned()->nullable()->change();
|
||||
$table->integer('parent_id')->nullable()->index();
|
||||
$table->string('description', 150)->nullable();
|
||||
$table->string('mime', 100)->nullable()->change();
|
||||
$table->string('extension', 10)->nullable()->change();
|
||||
$table->string('password', 50)->nullable();
|
||||
$table->string('type', 20)->nullable()->index();
|
||||
$table->timestamp('deleted_at')->nullable()->index();
|
||||
$table->dropColumn('url');
|
||||
$table->dropColumn('thumbnail_url');
|
||||
$table->renameColumn('path', 'public_path');
|
||||
$table->string('user_id')->index()->nullable()->change();
|
||||
|
||||
$table->index('updated_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->dropColumn('parent_id');
|
||||
$table->dropColumn('description');
|
||||
$table->dropColumn('password');
|
||||
$table->dropColumn('deleted_at');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddFolderPathColumnToFileEntriesTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasColumn('file_entries', 'path')) return;
|
||||
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->string('path')->nullable()->index();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->dropColumn('path');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Common\Files\FileEntry;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class MigrateFileEntryUsersToManyToMany extends Migration
|
||||
{
|
||||
/**
|
||||
* migrate file entries => user from "one to one" to "many to many"
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( ! Schema::hasTable('user_file_entry')) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileEntry::select('id', 'user_id')->orderBy('id')->chunk(50, function(Collection $entries) {
|
||||
$records = $entries->map(function(FileEntry $entry) {
|
||||
return ['file_entry_id' => $entry->id, 'user_id' => $entry->user_id, 'owner' => 1];
|
||||
});
|
||||
|
||||
DB::table('user_file_entry')->insert($records->toArray());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class MoveUploadsIntoSubfolders extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$drive = Storage::drive(config('common.site.uploads_disk'));
|
||||
|
||||
foreach ($drive->files() as $fileName) {
|
||||
$pathinfo = pathinfo($fileName);
|
||||
if ( ! isset($pathinfo['extension']) && ! \Str::contains($fileName, '.')) {
|
||||
$drive->createDir("$fileName-temp");
|
||||
$drive->move($fileName, "$fileName-temp/$fileName");
|
||||
$drive->rename("$fileName-temp", $fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
28
common/Database/migrations/2018_08_31_104145_rename_uploadables_table.php
Executable file
28
common/Database/migrations/2018_08_31_104145_rename_uploadables_table.php
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RenameUploadablesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::rename('uploadables', 'file_entry_models');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::rename('file_entry_models', 'uploadables');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RenameFileEntryModelsTableColumns extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('file_entry_models', function (Blueprint $table) {
|
||||
$table->renameColumn('upload_id', 'file_entry_id');
|
||||
$table->renameColumn('uploadable_id', 'model_id');
|
||||
$table->renameColumn('uploadable_type', 'model_type');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entry_models', function (Blueprint $table) {
|
||||
$table->renameColumn('file_entry_id', 'upload_id');
|
||||
$table->renameColumn('model_id', 'uploadable_id');
|
||||
$table->renameColumn('model_type', 'uploadable_type');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddTypeAndTitleColumnsToPagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('pages', function (Blueprint $table) {
|
||||
if ( ! Schema::hasColumn('pages', 'type')) {
|
||||
$table->string('type', 20)->index()->default('default')->after('slug');
|
||||
}
|
||||
|
||||
if ( ! Schema::hasColumn('pages', 'title')) {
|
||||
$table->string('title')->nullable()->after('id');
|
||||
} else {
|
||||
$table->string('title')->nullable()->change();
|
||||
}
|
||||
|
||||
$table->text('meta')->nullable()->after('slug');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('pages', function (Blueprint $table) {
|
||||
$table->dropColumn('type');
|
||||
$table->dropColumn('title');
|
||||
$table->dropColumn('meta');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class ChangeUniqueIndexOnTagsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('tags', function (Blueprint $table) {
|
||||
$table->string('type', 30)->default('custom')->change();
|
||||
|
||||
$sm = Schema::getConnection()->getDoctrineSchemaManager();
|
||||
$indexesFound = $sm->listTableIndexes('tags');
|
||||
|
||||
if (array_key_exists('tags_name_unique', $indexesFound)) {
|
||||
$table->dropUnique('tags_name_unique');
|
||||
}
|
||||
|
||||
if (array_key_exists('tags_name_type_unique', $indexesFound)) {
|
||||
$table->dropUnique('tags_name_type_unique');
|
||||
}
|
||||
|
||||
$table->unique(['name', 'type']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
28
common/Database/migrations/2019_02_16_150049_delete_old_seo_settings.php
Executable file
28
common/Database/migrations/2019_02_16_150049_delete_old_seo_settings.php
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class DeleteOldSeoSettings extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
DB::table('settings')->where('name', 'LIKE', 'seo.%')->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
38
common/Database/migrations/2019_02_24_141457_create_jobs_table.php
Executable file
38
common/Database/migrations/2019_02_24_141457_create_jobs_table.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateJobsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('queue');
|
||||
$table->longText('payload');
|
||||
$table->unsignedTinyInteger('attempts');
|
||||
$table->unsignedInteger('reserved_at')->nullable();
|
||||
$table->unsignedInteger('available_at');
|
||||
$table->unsignedInteger('created_at');
|
||||
|
||||
$table->index(['queue', 'reserved_at']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPreviewTokenToFileEntriesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->string('preview_token', 15)->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->dropColumn('preview_token');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddThumbnailColumnToFileEntriesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->boolean('thumbnail')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->dropColumn('thumbnail');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPaypalIdColumnToBillingPlansTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('billing_plans', function (Blueprint $table) {
|
||||
$table->string('paypal_id', 50)->nullable()->after('uuid');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('billing_plans', function (Blueprint $table) {
|
||||
$table->dropColumn('paypal_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class IndexDescriptionColumnInFileEntriesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->index('description');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
36
common/Database/migrations/2019_06_08_120504_create_custom_domains_table.php
Executable file
36
common/Database/migrations/2019_06_08_120504_create_custom_domains_table.php
Executable file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateCustomDomainsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('custom_domains')) return;
|
||||
|
||||
Schema::create('custom_domains', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('host', 100)->index()->unique();
|
||||
$table->integer('user_id')->index();
|
||||
$table->timestamp('created_at')->index()->nullable();
|
||||
$table->timestamp('updated_at')->index()->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('custom_domains');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddUserIdColumnToPagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('pages', function (Blueprint $table) {
|
||||
$table->integer('user_id')->nullable()->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('pages', function (Blueprint $table) {
|
||||
$table->dropColumn('user_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RenamePagesTableToCustomPages extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( ! Schema::hasTable('custom_pages')) {
|
||||
Schema::table('pages', function (Blueprint $table) {
|
||||
$table->rename('custom_pages');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
if ( ! Schema::hasTable('pages')) {
|
||||
Schema::table('custom_pages', function (Blueprint $table) {
|
||||
$table->rename('pages');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
38
common/Database/migrations/2019_06_18_133933_create_permissions_table.php
Executable file
38
common/Database/migrations/2019_06_18_133933_create_permissions_table.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePermissionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('permissions')) return;
|
||||
|
||||
Schema::create('permissions', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name', 30)->unique();
|
||||
$table->string('display_name');
|
||||
$table->text('description')->nullable();
|
||||
$table->string('group', 30);
|
||||
$table->text('restrictions')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('permissions');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePermissionablesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('permissionables')) return;
|
||||
|
||||
Schema::create('permissionables', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('permission_id')->index();
|
||||
$table->integer('permissionable_id')->index();
|
||||
$table->string('permissionable_type', 40)->index();
|
||||
$table->text('restrictions')->nullable();
|
||||
|
||||
$table->unique(['permission_id', 'permissionable_id', 'permissionable_type'], 'permissionable_unique');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('permissionables');
|
||||
}
|
||||
}
|
||||
41
common/Database/migrations/2019_06_18_135822_rename_permissions_columns.php
Executable file
41
common/Database/migrations/2019_06_18_135822_rename_permissions_columns.php
Executable file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RenamePermissionsColumns extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
$tables = ['users', 'roles', 'billing_plans'];
|
||||
|
||||
foreach ($tables as $tableName) {
|
||||
// rename permissions column
|
||||
if (Schema::hasColumn($tableName, 'permissions')) {
|
||||
Schema::table($tableName, function (Blueprint $table) {
|
||||
$table->renameColumn('permissions', 'legacy_permissions');
|
||||
});
|
||||
}
|
||||
|
||||
// drop permissions index, if exists
|
||||
Schema::table($tableName, function (Blueprint $table) use($tableName) {
|
||||
$sm = Schema::getConnection()->getDoctrineSchemaManager();
|
||||
$indexesFound = $sm->listTableIndexes($tableName);
|
||||
|
||||
if (array_key_exists('legacy_permissions', $indexesFound)) {
|
||||
$table->dropIndex('legacy_permissions');
|
||||
}
|
||||
|
||||
if (array_key_exists('permissions', $indexesFound)) {
|
||||
$table->dropIndex('permissions');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
39
common/Database/migrations/2019_07_08_122001_create_css_themes_table.php
Executable file
39
common/Database/migrations/2019_07_08_122001_create_css_themes_table.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateCssThemesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('css_themes')) return;
|
||||
|
||||
Schema::create('css_themes', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name', 100)->unique();
|
||||
$table->boolean('is_dark')->default(0);
|
||||
$table->boolean('default_light')->index()->default(0);
|
||||
$table->boolean('default_dark')->index()->default(0);
|
||||
$table->integer('user_id')->index();
|
||||
$table->text('colors');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('css_themes');
|
||||
}
|
||||
}
|
||||
37
common/Database/migrations/2019_07_20_141752_create_invoices_table.php
Executable file
37
common/Database/migrations/2019_07_20_141752_create_invoices_table.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateInvoicesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('invoices')) return;
|
||||
|
||||
Schema::create('invoices', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('subscription_id')->index();
|
||||
$table->boolean('paid');
|
||||
$table->string('uuid', 10)->index();
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('invoices');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddGlobalColumnToCustomDomainsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasColumn('custom_domains', 'global')) return;
|
||||
Schema::table('custom_domains', function (Blueprint $table) {
|
||||
$table->boolean('global')->index()->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('custom_domains', function (Blueprint $table) {
|
||||
$table->dropColumn('global');
|
||||
});
|
||||
}
|
||||
}
|
||||
33
common/Database/migrations/2019_09_13_141123_change_plan_amount_to_float.php
Executable file
33
common/Database/migrations/2019_09_13_141123_change_plan_amount_to_float.php
Executable file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class ChangePlanAmountToFloat extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('billing_plans', function(Blueprint $table) {
|
||||
$prefix = DB::getTablePrefix();
|
||||
DB::statement("ALTER TABLE {$prefix}billing_plans CHANGE amount amount DECIMAL(13,2) NULL");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('billing_plans', function (Blueprint $table) {
|
||||
$table->integer('amount')->nullable()->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddIndexToUsernameColumn extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->index('username');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropIndex('username');
|
||||
});
|
||||
}
|
||||
}
|
||||
39
common/Database/migrations/2019_10_20_143522_create_comments_table.php
Executable file
39
common/Database/migrations/2019_10_20_143522_create_comments_table.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateCommentsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('comments')) return;
|
||||
|
||||
Schema::create('comments', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->text('content');
|
||||
$table->integer('parent_id')->unsigned()->nullable()->index();
|
||||
$table->string('path')->index();
|
||||
$table->integer('user_id')->unsigned()->index();
|
||||
$table->integer('commentable_id')->unsigned()->index();
|
||||
$table->string('commentable_type', 30)->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('comments');
|
||||
}
|
||||
}
|
||||
37
common/Database/migrations/2019_10_23_134520_create_notifications_table.php
Executable file
37
common/Database/migrations/2019_10_23_134520_create_notifications_table.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateNotificationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('notifications')) return;
|
||||
|
||||
Schema::create('notifications', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('type');
|
||||
$table->morphs('notifiable');
|
||||
$table->text('data');
|
||||
$table->timestamp('read_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('notifications');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddResourceIdAndTypeToCustomDomainsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('custom_domains', function (Blueprint $table) {
|
||||
$table->integer('resource_id')->unsigned()->index()->nullable();
|
||||
$table->string('resource_type', 20)->index()->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('custom_domains', function (Blueprint $table) {
|
||||
$table->dropColumn('resource_id');
|
||||
$table->dropColumn('resource_type');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePersonalAccessTokensTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('personal_access_tokens')) return;
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->morphs('tokenable');
|
||||
$table->string('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class RenamePublicPathColumnToDiskPrefix extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$prefix = DB::getTablePrefix();
|
||||
DB::statement("ALTER TABLE {$prefix}file_entries CHANGE public_path disk_prefix VARCHAR(191) NULL");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class ChangeFileSizeColumnDefaultValueTo0 extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
$table->bigInteger('file_size')->unsigned()->default(0)->notNull()->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('file_entries', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdateFileEntryModelsTableToV2 extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('file_entry_models', function (Blueprint $table) {
|
||||
$table->timestamps();
|
||||
$table->boolean('owner')->default(0)->index();
|
||||
$table->text('permissions')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class MoveUserFileEntryTableRecordsToFileEntryModels extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( ! Schema::hasTable('user_file_entry')) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::table('user_file_entry')
|
||||
->chunkById(100, function(Collection $records) {
|
||||
$records = $records->map(function($record) {
|
||||
$record = (array) $record;
|
||||
$record['model_type'] = User::class;
|
||||
$record['model_id'] = $record['user_id'];
|
||||
unset($record['user_id']);
|
||||
return $record;
|
||||
});
|
||||
try {
|
||||
DB::table('file_entry_models')->insert($records->toArray());
|
||||
} catch (Exception $e) {
|
||||
//
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateNotificationSubscriptionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('notification_subscriptions')) return;
|
||||
|
||||
Schema::create('notification_subscriptions', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('notif_id', 5)->index();
|
||||
$table->integer('user_id')->index();
|
||||
$table->string('channels');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('notification_subscriptions');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddLanguageColToLocalizationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('localizations', function (Blueprint $table) {
|
||||
$table->string('language', 5)->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('localizations', function (Blueprint $table) {
|
||||
$table->dropColumn('language');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Common\Core\Values\ValueLists;
|
||||
use Common\Localizations\Localization;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class AddLangCodeToExistingLocalizations extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$languages = app(ValueLists::class)->languages();
|
||||
|
||||
app(Localization::class)->get()->each(function(Localization $localization) use($languages) {
|
||||
if ( ! $localization->language) {
|
||||
$lang = Arr::first($languages, function($lang) use($localization) {
|
||||
return slugify($lang['name']) === slugify($localization->name);
|
||||
});
|
||||
if ( ! $lang) {
|
||||
$lang = Arr::random($languages);
|
||||
}
|
||||
$localization->language = $lang['code'];
|
||||
$localization->save();
|
||||
}
|
||||
|
||||
$slugName = slugify($localization->name);
|
||||
$oldPath = resource_path("lang/$slugName.json");
|
||||
|
||||
if (file_exists($oldPath)) {
|
||||
rename($oldPath, resource_path("lang/$localization->language.json"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddHiddenColumnToPlansTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('billing_plans', function (Blueprint $table) {
|
||||
$table->boolean('hidden')->default(false)->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('billing_plans', function (Blueprint $table) {
|
||||
$table->dropColumn('hidden');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class AddVerifiedAtColumnToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( !Schema::hasColumn('users', 'email_verified_at')) {
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('email_verified_at');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class MoveConfirmedColumnToEmailVerifiedAt extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasColumn('users', 'confirmed')) {
|
||||
User::where('confirmed', true)
|
||||
->update(['email_verified_at' => Carbon::now()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class FixIssuesWithMigrationToLaravel7 extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
try {
|
||||
collect(File::allFiles(resource_path('views/vendor')))
|
||||
->filter(function(SplFileInfo $file) {
|
||||
return Str::endsWith($file->getPathname(), 'blade.php') &&
|
||||
!Str::endsWith($file->getPathname(), 'html/message.blade.php') &&
|
||||
!Str::endsWith($file->getPathname(), 'email.blade.php');
|
||||
})->each(function(SplFileInfo $file) {
|
||||
File::delete($file->getPathname());
|
||||
});
|
||||
} catch (Exception $e) {
|
||||
//
|
||||
}
|
||||
|
||||
try {
|
||||
File::delete(base_path('vendor/symfony/translation/TranslatorInterface.php'));
|
||||
} catch (Exception $e) {
|
||||
//
|
||||
}
|
||||
|
||||
try {
|
||||
$setting = DB::table('settings')->where('name', 'player.enable_landing')->first();
|
||||
if ($setting && (bool) $setting->value) {
|
||||
DB::table('settings')->where('name', 'homepage.type')->orWhere('name', 'homepage.value')->delete();
|
||||
DB::table('settings')->insert([
|
||||
['name' => 'homepage.type', 'value' => 'component'],
|
||||
['name' => 'homepage.value', 'value' => 'Landing Page'],
|
||||
]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
36
common/Database/migrations/2020_07_22_165126_create_workspaces_table.php
Executable file
36
common/Database/migrations/2020_07_22_165126_create_workspaces_table.php
Executable file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateWorkspacesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( ! Schema::hasTable('workspaces')) {
|
||||
Schema::create('workspaces', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->integer('owner_id')->unsigned()->index();
|
||||
$table->timestamp('created_at')->index()->nullable();
|
||||
$table->timestamp('updated_at')->index()->nullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('workspaces');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateWorkspaceInvitesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( ! Schema::hasTable('workspace_invites')) {
|
||||
Schema::create('workspace_invites', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('avatar', 80)->nullable();
|
||||
$table->integer('workspace_id')->unsigned()->index();
|
||||
$table->integer('user_id')->unsigned()->index()->nullable();
|
||||
$table->string('email', 80)->index();
|
||||
$table->integer('role_id')->unsigned()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('workspace_invites');
|
||||
}
|
||||
}
|
||||
39
common/Database/migrations/2020_07_23_164502_create_workspace_user_table.php
Executable file
39
common/Database/migrations/2020_07_23_164502_create_workspace_user_table.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateWorkspaceUserTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ( ! Schema::hasTable('workspace_user')) {
|
||||
Schema::create('workspace_user', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('user_id')->unsigned()->index();
|
||||
$table->integer('workspace_id')->unsigned()->index();
|
||||
$table->integer('role_id')->unsigned()->index()->nullable();
|
||||
$table->boolean('is_owner')->index()->default(false);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['workspace_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('workspace_user');
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user