locale pages, clone pages and objects, some other stuff
parent
c478fa1636
commit
49d8143062
|
|
@ -19,7 +19,7 @@ class PagesController extends Controller {
|
|||
}
|
||||
|
||||
public function root(Request $request): JsonResponse {
|
||||
return fractal(Page::root(), new PageTransformer())->respond();
|
||||
return fractal(Page::root($request->get('with_trashed')), new PageTransformer())->respond();
|
||||
}
|
||||
|
||||
public function find(Request $request): ?JsonResponse {
|
||||
|
|
@ -52,6 +52,12 @@ class PagesController extends Controller {
|
|||
return fractal($model, new PageTransformer())->respond();
|
||||
}
|
||||
|
||||
public function clone(Request $request, $id): JsonResponse {
|
||||
$model = $this->model->byUuid($id)->firstOrFail();
|
||||
$parent = $this->model->byUuid($request->get('parent'))->first();
|
||||
$clone = $model->clone($parent, $request->get('recursive'));
|
||||
return fractal($clone, new PageTransformer())->respond();
|
||||
}
|
||||
|
||||
|
||||
public function store(Request $request): void {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
namespace App\Models\Localization;
|
||||
|
||||
use App\Models\Pages\Page;
|
||||
use App\Support\RelationValuesTrait;
|
||||
use App\Support\UuidScopeTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
|
|
@ -28,10 +30,22 @@ class Locale extends Model {
|
|||
];
|
||||
|
||||
|
||||
public function page(): BelongsTo {
|
||||
return $this->belongsTo(Page::class, 'name', 'slug');
|
||||
}
|
||||
|
||||
|
||||
public function scopeEnabled($query) {
|
||||
return $query->where(['is_enabled' => true]);
|
||||
}
|
||||
|
||||
public function scopeDefault($query) {
|
||||
return $query->where(['name' => env('APP_LOCALE')]);
|
||||
}
|
||||
public function scopeNotDefault($query) {
|
||||
return $query->where('name', '!=', env('APP_LOCALE'));
|
||||
}
|
||||
|
||||
|
||||
public function getIsActiveAttribute(): bool {
|
||||
return $this->name === App::getLocale();
|
||||
|
|
@ -49,13 +63,4 @@ class Locale extends Model {
|
|||
}
|
||||
|
||||
|
||||
public function setAsDefault(): bool {
|
||||
self::query()->where(['is_default' => true])->update(['is_default' => false]);
|
||||
return $this->update(['is_default' => true]);
|
||||
}
|
||||
|
||||
public static function default() {
|
||||
return self::query()->where(['is_default' => 1])->first();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ class NirObject extends Model {
|
|||
$clone->setValue($field->name, $this->getValue($field->name));
|
||||
});
|
||||
});
|
||||
$clone->copyObjectsFromObjectable($this);
|
||||
return $clone;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use App\Models\User;
|
|||
use App\Support\HasObjectsTrait;
|
||||
use App\Support\RelationValuesTrait;
|
||||
use App\Support\UuidScopeTrait;
|
||||
use DragonCode\Support\Facades\Helpers\Str;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
|
@ -52,7 +53,7 @@ class Page extends Model {
|
|||
}
|
||||
|
||||
public function siblings(): HasMany {
|
||||
return $this->hasMany(Page::class, 'parent_id', 'parent_id');
|
||||
return $this->hasMany(Page::class, 'parent_id', 'parent_id')->where('id', '!=', $this->id);
|
||||
}
|
||||
|
||||
public function picture(): BelongsTo {
|
||||
|
|
@ -123,18 +124,34 @@ class Page extends Model {
|
|||
}
|
||||
|
||||
|
||||
public static function byUrl($url) {
|
||||
if ($url === '/') return Page::query()->where(['parent_id' => 0, 'slug' => ''])->first();
|
||||
elseif ($url = trim($url, '/ ')) {
|
||||
$query = self::query();
|
||||
collect(explode('/', $url))->reverse()->values()->each(function($slug, $index) use($query) {
|
||||
if ($slug !== '') {
|
||||
$index ? $query->nthParentSlug($index, $slug) : $query->bySlug($slug);
|
||||
}
|
||||
});
|
||||
return $query->first();
|
||||
}
|
||||
return null;
|
||||
public function checkConflictedProps($props = ['name', 'slug']) {
|
||||
collect($props)->each(function($prop) {
|
||||
$this->checkConflictedProp($prop);
|
||||
});
|
||||
}
|
||||
public function checkConflictedProp($prop): ?bool {
|
||||
if ($val = $this->$prop) {
|
||||
$i = 1;
|
||||
while($this->siblings()->where([$prop => $val])->exists()) {
|
||||
$val = "{$this->$prop} (копия {$i})";
|
||||
if ($prop === 'slug') $val = Str::slug($val);
|
||||
$i++;
|
||||
}
|
||||
return ($this->$prop !== $val) ? $this->update([$prop => $val]) : null;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
|
||||
public function clone(?Page $parent = null, $recursive = false): Model {
|
||||
$model = $this->replicate(['uuid']);
|
||||
if ($parent) $model->parent_id = $parent->id;
|
||||
$model->push();
|
||||
$model->copyObjectsFromObjectable($this);
|
||||
$model->checkConflictedProps();
|
||||
if ($recursive) $this->children->each(function($child) use($model) {
|
||||
$child->clone($model, true);
|
||||
});
|
||||
return $model;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -145,6 +162,7 @@ class Page extends Model {
|
|||
} else $this->moveSet('forward', $ord, null, $parent);
|
||||
$this->update(['parent_id' => $parent->id ?? 0, 'ord' => $ord]);
|
||||
$this->trimIndexes([$prevParent->id ?? 0, $parent->id ?? 0]);
|
||||
$this->checkConflictedProps();
|
||||
}
|
||||
public function moveSet($dir = 'forward', $ordFrom = null, $ordTo = null, ?Page $parent = null) {
|
||||
$query = Page::query()->where(['parent_id' => $parent->id ?? 0])->orderBy('ord');
|
||||
|
|
@ -168,8 +186,25 @@ class Page extends Model {
|
|||
}
|
||||
|
||||
|
||||
public static function root() {
|
||||
return self::query()->where(['parent_id' => 0])->orderBy('ord')->get();
|
||||
public static function byUrl($url) {
|
||||
if ($url === '/') return Page::query()->where(['parent_id' => 0, 'slug' => ''])->first();
|
||||
elseif ($url = trim($url, '/ ')) {
|
||||
$query = self::query();
|
||||
collect(explode('/', $url))->reverse()->values()->each(function($slug, $index) use($query) {
|
||||
if ($slug !== '') {
|
||||
$index ? $query->nthParentSlug($index, $slug) : $query->bySlug($slug);
|
||||
}
|
||||
});
|
||||
return $query->first();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static function root($withTrashed = false) {
|
||||
$query = self::query()->where(['parent_id' => 0])->orderBy('ord');
|
||||
if ($withTrashed) $query->withTrashed();
|
||||
return $query->get();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Forms\Pages;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\Localization\Locale;
|
||||
use App\Models\Objects\FieldType;
|
||||
use App\Models\Pages\Page;
|
||||
use App\Models\Pages\PageType;
|
||||
use App\Services\Forms\FormsService;
|
||||
use App\Transformers\Assets\AssetTransformer;
|
||||
use App\Transformers\Localization\LocaleTransformer;
|
||||
use App\Transformers\Pages\PageTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Spatie\Fractal\Fractal;
|
||||
|
||||
class LocalePageForms extends FormsService {
|
||||
public array $formTitles = ['create' => 'Создание страницы локализации', 'update' => 'Редактирование страницы локализации'];
|
||||
|
||||
public function form(?string $id = null, array $data = []): array {
|
||||
$model = Page::byUuid($id)->first();
|
||||
$groups = [
|
||||
['name' => 'common', 'title' => 'Основные свойства страницы', 'fields' => $this->commonGroupFields($model)],
|
||||
['name' => 'seo', 'title' => 'Мета данные', 'fields' => $this->seoGroupFields($model)]
|
||||
];
|
||||
return ['title' => $this->formTitle($model), 'data' => $groups];
|
||||
}
|
||||
|
||||
public function commonGroupFields(?Page $model): array {
|
||||
$fields = [
|
||||
[
|
||||
'name' => 'locale',
|
||||
'title' => 'Язык',
|
||||
'type' => FieldType::RELATION,
|
||||
'required' => true,
|
||||
'options' => $this->getLocales()
|
||||
],
|
||||
[
|
||||
'name' => 'picture',
|
||||
'title' => 'Изображение',
|
||||
'type' => FieldType::IMAGE,
|
||||
'value' => ($v = $model->picture ?? null) ? fractal($v, new AssetTransformer()) : null
|
||||
]
|
||||
];
|
||||
return ['data' => $fields];
|
||||
}
|
||||
|
||||
public function seoGroupFields(?Page $model): array {
|
||||
$fields = [
|
||||
[
|
||||
'name' => 'h1',
|
||||
'title' => 'Заголовок H1',
|
||||
'type' => FieldType::STRING,
|
||||
'value' => $model->h1 ?? null
|
||||
],
|
||||
[
|
||||
'name' => 'title',
|
||||
'title' => 'Title страницы',
|
||||
'type' => FieldType::STRING,
|
||||
'value' => $model->title ?? null
|
||||
],
|
||||
[
|
||||
'name' => 'description',
|
||||
'title' => 'Описание',
|
||||
'type' => FieldType::TEXT,
|
||||
'value' => $model->description ?? null
|
||||
],
|
||||
[
|
||||
'name' => 'keywords',
|
||||
'title' => 'Ключевые слова',
|
||||
'type' => FieldType::STRING,
|
||||
'value' => $model->keywords ?? null
|
||||
],
|
||||
|
||||
];
|
||||
return ['data' => $fields];
|
||||
}
|
||||
|
||||
|
||||
public function getLocales(): Fractal {
|
||||
return fractal(Locale::query()->notDefault()->doesntHave('page')->get(), new LocaleTransformer());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function store(array $data): ?JsonResponse {
|
||||
$locale = Locale::byUuid($data['locale'] ?? null)->firstOrFail();
|
||||
$data['picture_id'] = ($v = Asset::byUuid($data['picture'] ?? null)->first()) ? $v->id : null;
|
||||
$data += ['type' => PageType::CONTENT, 'name' => $locale->title, 'slug' => $locale->name, 'ord' => Page::query()->where(['parent_id' => 0])->max('ord') + 1];
|
||||
$model = Page::query()->where(['parent_id' => 0, 'slug' => $locale->name])->withTrashed()->first() ?? Page::create($data);
|
||||
$model->restore();
|
||||
return fractal($model, new PageTransformer())->respond();
|
||||
}
|
||||
|
||||
public function update(string $id, array $data): ?JsonResponse {
|
||||
$model = Page::byUuid($id)->firstOrFail();
|
||||
$data['picture_id'] = ($v = Asset::byUuid($data['picture'] ?? null)->first()) ? $v->id : null;
|
||||
$model->update($data);
|
||||
return fractal($model->fresh(), new PageTransformer())->respond();
|
||||
}
|
||||
}
|
||||
|
|
@ -50,6 +50,7 @@ class PageForms extends FormsService {
|
|||
'title' => 'Адрес',
|
||||
'type' => FieldType::STRING,
|
||||
'hidden' => !$model,
|
||||
'readonly' => !($model->parent_id ?? true),
|
||||
'value' => $model->slug ?? null
|
||||
],
|
||||
[
|
||||
|
|
@ -123,9 +124,10 @@ class PageForms extends FormsService {
|
|||
$parent = Page::byUuid($data['parent'] ?? null)->first();
|
||||
$data['parent_id'] = $parent->id ?? 0;
|
||||
$data['picture_id'] = ($v = Asset::byUuid($data['picture'] ?? null)->first()) ? $v->id : null;
|
||||
$data['slug'] = $data['slug'] ?? Str::slug(Str::transliterate($data['name'] ?? null));
|
||||
$data['slug'] = Str::slug($data['slug'] ?? $data['name'] ?? 'unnamed');
|
||||
$model = Page::create($data);
|
||||
$model->update(['ord' => $model->getMaxOrd()]);
|
||||
$model->checkConflictedProps();
|
||||
if ($model->type === PageType::REGISTRY) $model->registry->update(['type' => $data['registry_type'] ?? RegistryType::SIMPLE]);
|
||||
return fractal($model, new PageTransformer())->respond();
|
||||
}
|
||||
|
|
@ -133,8 +135,10 @@ class PageForms extends FormsService {
|
|||
public function update(string $id, array $data): ?JsonResponse {
|
||||
$model = Page::byUuid($id)->firstOrFail();
|
||||
$data['picture_id'] = ($v = Asset::byUuid($data['picture'] ?? null)->first()) ? $v->id : null;
|
||||
$data['slug'] = $data['slug'] ?? Str::slug(Str::transliterate($data['name'] ?? null));
|
||||
if ($model->parent_id) $data['slug'] = Str::slug($data['slug'] ?? $data['name'] ?? 'unnamed');
|
||||
else unset($data['slug']);
|
||||
$model->update($data);
|
||||
$model->checkConflictedProps();
|
||||
if ($model->type === PageType::REGISTRY) $model->registry->update(['type' => $data['registry_type'] ?? RegistryType::SIMPLE]);
|
||||
return fractal($model->fresh(), new PageTransformer())->respond();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Services\Forms\Pages;
|
|||
|
||||
class PageFormsServices {
|
||||
public static array $services = [
|
||||
'page' => PageForms::class
|
||||
'page' => PageForms::class,
|
||||
'localePage' => LocalePageForms::class
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ trait HasObjectsTrait {
|
|||
($ord > $currentOrd) ? $this->moveObjectsSet('backward', $currentOrd, $ord, $group) : $this->moveObjectsSet('forward', $ord, $currentOrd, $group);
|
||||
} else $this->moveObjectsSet('forward', $ord, null, $group);
|
||||
$this->objects()->updateExistingPivot($object, ['ord' => $ord, 'group' => $group ?? 'default']);
|
||||
$this->trimIndexes([$group, $currentGroup]);
|
||||
$this->trimObjectIndexes([$group, $currentGroup]);
|
||||
}
|
||||
|
||||
public function moveObjectsSet($dir = 'forward', $ordFrom = null, $ordTo = null, $group = null) {
|
||||
|
|
@ -70,7 +70,7 @@ trait HasObjectsTrait {
|
|||
});
|
||||
}
|
||||
|
||||
public function trimIndexes($groups) {
|
||||
public function trimObjectIndexes($groups) {
|
||||
collect(is_array($groups) ? $groups : [$groups])->unique()->each(function($group) {
|
||||
$this->objectsByGroup($group)->orderByPivot('ord')->each(function($object, $index) {
|
||||
if ($object->pivot->ord !== $index) $this->objects()->updateExistingPivot($object, ['ord' => $index]);
|
||||
|
|
@ -79,6 +79,13 @@ trait HasObjectsTrait {
|
|||
}
|
||||
|
||||
|
||||
public function copyObjectsFromObjectable($objectable) {
|
||||
$objectable->objects->each(function($object) {
|
||||
if ($clone = $object->clone()) $this->objects()->attach($clone->id, ['ord' => $object->pivot->ord, 'group' => $object->pivot->group]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function getValue($fieldName) {
|
||||
return $this->object ? $this->object->getValue($fieldName) : null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,12 +32,13 @@ class PageTransformer extends TransformerAbstract {
|
|||
'sub_type' => $model->sub_type,
|
||||
'name' => $model->name,
|
||||
'title' => $model->title,
|
||||
'h1' => $model->h1,
|
||||
'description' => $model->description,
|
||||
'keywords' => $model->keywords,
|
||||
'h1' => $model->h1,
|
||||
'has_children' => $model->children()->exists(),
|
||||
'created_at' => $model->created_at ? $model->created_at->toIso8601String() : null,
|
||||
'updated_at' => $model->updated_at ? $model->updated_at->toIso8601String() : null
|
||||
'updated_at' => $model->updated_at ? $model->updated_at->toIso8601String() : null,
|
||||
'deleted_at' => $model->deleted_at ? $model->deleted_at->toIso8601String() : null
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +46,10 @@ class PageTransformer extends TransformerAbstract {
|
|||
return $this->collection($model->children, new PageTransformer());
|
||||
}
|
||||
|
||||
public function includeChildrenWithTrashed(Page $model): Collection {
|
||||
return $this->collection($model->children()->withTrashed()->get, new PageTransformer());
|
||||
}
|
||||
|
||||
public function includeParent(Page $model): ?Item {
|
||||
return $model->parent ? $this->item($model->parent, new PageTransformer()) : null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,13 @@ use Illuminate\Database\Seeder;
|
|||
|
||||
class LocalesTableSeeder extends Seeder {
|
||||
public array $locales = [
|
||||
'ru' => ['title' => 'Русский', 'is_enabled' => true, 'is_default' => true],
|
||||
'en' => ['title' => 'English', 'is_enabled' => true],
|
||||
'ua' => ['title' => 'Українська', 'is_enabled' => true]
|
||||
'ru' => ['title' => 'Русский'],
|
||||
'en' => ['title' => 'Английский'],
|
||||
'de' => ['title' => 'Немецкий'],
|
||||
'fr' => ['title' => 'Французский'],
|
||||
'it' => ['title' => 'Итальянский'],
|
||||
'es' => ['title' => 'Испанский'],
|
||||
'cn' => ['title' => 'Китайский']
|
||||
];
|
||||
|
||||
public function run() {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class PagesTableSeeder extends Seeder {
|
|||
'Структура ТК' => ['type' => PageType::TK_STRUCTURE]
|
||||
]
|
||||
],
|
||||
'Main page' => [
|
||||
'Агнлийский' => [
|
||||
'slug' => 'en',
|
||||
'children' => [
|
||||
'Content page' => [
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ Route::group(['prefix' => 'pages'], function() {
|
|||
Route::get('/find', 'Api\Pages\PagesController@find');
|
||||
Route::get('/{id}', 'Api\Pages\PagesController@show');
|
||||
Route::group(['middleware' => ['auth:api']], function() {
|
||||
Route::put('/{id}', 'Api\Pages\PagesController@move');
|
||||
Route::put('/move/{id}', 'Api\Pages\PagesController@move');
|
||||
Route::put('/clone/{id}', 'Api\Pages\PagesController@clone');
|
||||
Route::delete('/{id}', 'Api\Pages\PagesController@destroy');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue