basic pages functions

master
Константин 2023-05-30 19:33:40 +03:00
parent 667b13c76c
commit cf18dea146
16 changed files with 494 additions and 35 deletions

View File

@ -0,0 +1,47 @@
<?php
namespace App\Http\Controllers\Api\Pages;
use App\Http\Controllers\Controller;
use App\Models\Pages\Page;
use App\Transformers\Pages\PageTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class PagesController extends Controller {
protected Page $model;
public function __construct(Page $model) {
$this->model = $model;
}
public function root(Request $request): JsonResponse {
return fractal(Page::root(), new PageTransformer())->respond();
}
public function index(Request $request): JsonResponse {
$query = $this->model->query();
$paginator = $query->paginate(config('app.pagination_limit'));
return fractal($paginator, new PageTransformer())->respond();
}
public function show($id): JsonResponse {
$model = $this->model->byUuid($id)->firstOrFail();
return fractal($model, new PageTransformer())->respond();
}
public function store(Request $request): void {
}
public function update(Request $request, $uuid): void {
}
public function destroy(Request $request, $uuid): JsonResponse {
$model = $this->model->byUuid($uuid)->firstOrFail();
$model->delete();
return response()->json(null, 204);
}
}

88
app/Models/Pages/Page.php Normal file
View File

@ -0,0 +1,88 @@
<?php
namespace App\Models\Pages;
use App\Support\HasObjectsTrait;
use App\Support\RelationValuesTrait;
use App\Support\UuidScopeTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
class Page extends Model {
use UuidScopeTrait, SoftDeletes, HasObjectsTrait, RelationValuesTrait;
protected $dates = [
];
protected $fillable = [
'uuid',
'parent_id',
'slug',
'type',
'name',
'title',
'h1',
'ord'
];
protected $hidden = [
'id'
];
public function parent(): BelongsTo {
return $this->belongsTo(Page::class, 'parent_id');
}
public function children(): HasMany {
return $this->hasMany(Page::class, 'parent_id');
}
public function sections(): MorphToMany {
return $this->objects()->whereHas('type.parent', function($query) {
$query->where(['name' => 'page-section']);
});
}
public function sidebar(): Model {
return $this->getObject('page-sidebar');
}
public function sidebars(): MorphToMany {
return $this->objects()->whereHas('type', function($query) {
$query->where(['name' => 'page-sidebar']);
});
}
public function getLinkAttribute(): string {
return $this->parents->reverse()->push($this)->pluck('slug')->implode('/');
}
public function getParentsAttribute(): Collection {
$page = $this;
$result = collect([]);
while($page = $page->parent) $result->push($page);
return $result;
}
public function getParsedTypeAttribute(): array {
return ['name' => $this->type, 'title' => PageType::TITLES[$this->type] ?? null];
}
public function addSection($typeName, $ord) {
$this->createObject($typeName, $ord);
}
public static function root() {
return self::query()->where(['parent_id' => 0])->get();
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models\Pages;
class PageType {
public const CONTENT = 'content';
public const TITLES = [
self::CONTENT => 'Контентная страница'
];
}

View File

@ -2,47 +2,18 @@
namespace App\Providers;
use App\Models\Advisories\Advisory;
use App\Models\Advisories\AdvisoryMember;
use App\Models\Advisories\AdvisorySession;
use App\Models\Advisories\SessionInvitation;
use App\Models\Applications\Application;
use App\Models\Asset;
use App\Models\Catalog\Category;
use App\Models\Classification\Classifier;
use App\Models\Classification\Code;
use App\Models\Companies\Address;
use App\Models\Companies\BankDetails;
use App\Models\Companies\Company;
use App\Models\Companies\CompanyMember;
use App\Models\Companies\Contact;
use App\Models\Companies\Department;
use App\Models\Correspondence\Letter;
use App\Models\Dictionaries\Dictionary;
use App\Models\Dictionaries\DictionaryItem;
use App\Models\Normatives\Development\Development;
use App\Models\Normatives\Development\DevelopmentStage;
use App\Models\Normatives\Normative;
use App\Models\Normatives\Plans\Plan;
use App\Models\Notes\Note;
use App\Models\Objects\Field;
use App\Models\Objects\FieldsGroup;
use App\Models\Objects\NirObject;
use App\Models\Objects\ObjectType;
use App\Models\Pages\Page;
use App\Models\Permission;
use App\Models\Polls\Poll;
use App\Models\Polls\PollInvitation;
use App\Models\Processes\Demand;
use App\Models\Processes\Participant;
use App\Models\Processes\Performer;
use App\Models\Processes\Process;
use App\Models\Processes\Scenario;
use App\Models\Processes\Stage;
use App\Models\Processes\Task;
use App\Models\Role;
use App\Models\SocialProvider;
use App\Models\User;
use App\Models\UserKey;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;
@ -82,7 +53,9 @@ class AppServiceProvider extends ServiceProvider
'object-field' => Field::class,
'fields-group' => FieldsGroup::class,
'object' => NirObject::class,
'object-type' => ObjectType::class
'object-type' => ObjectType::class,
'page' => Page::class
]);
}
}

View File

@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
trait HasObjectsTrait {
public function objects(): MorphToMany {
return $this->morphToMany(NirObject::class, 'objectable')->withTimestamps();
return $this->morphToMany(NirObject::class, 'objectable')->orderByPivot('ord')->withTimestamps();
}
public function getObjectAttribute() {
@ -21,8 +21,13 @@ trait HasObjectsTrait {
return ($type = ObjectType::query()->where(['name' => $typeName])->first()) ? $this->objects()->firstOrCreate(['type_id' => $type->id]) : null;
}
public function createObject($typeName): ?Model {
return ($type = ObjectType::query()->where(['name' => $typeName])->first()) ? $this->objects()->create(['type_id' => $type->id]) : null;
public function createObject($typeName, $ord = null): ?Model {
if (($type = ObjectType::query()->where(['name' => $typeName])->first()) && ($object = NirObject::create(['type_id' => $type->id]))) {
if ($ord !== null) $ord = ($res = $this->objects()->where(['type_id' => $type->id])->withPivot('ord')->reorder()->orderByPivot('ord', 'desc')->first()) ? ($res->pivot->ord + 1) : 0;
$this->objects()->attach($object->id, ['ord' => $ord]);
return $object;
}
return null;
}
public function getValue($fieldName) {

View File

@ -0,0 +1,57 @@
<?php
namespace App\Transformers\Pages;
use App\Models\Pages\Page;
use App\Services\PermissionsService;
use App\Transformers\Objects\ObjectTransformer;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
use League\Fractal\Resource\Primitive;
use League\Fractal\TransformerAbstract;
class PageTransformer extends TransformerAbstract {
protected array $defaultIncludes = [
];
protected array $availableIncludes = [
'children', 'parent', 'sections', 'sidebars', 'permissions'
];
public function transform(Page $model): array {
return [
'id' => $model->uuid,
'slug' => $model->slug,
'link' => $model->link,
'type' => $model->parsedType,
'name' => $model->name,
'title' => $model->title,
'h1' => $model->h1,
'created_at' => $model->created_at ? $model->created_at->toIso8601String() : null,
'updated_at' => $model->updated_at ? $model->updated_at->toIso8601String() : null
];
}
public function includeChildren(Page $model): Collection {
return $this->collection($model->children, new PageTransformer());
}
public function includeParent(Page $model): ?Item {
return $model->parent ? $this->item($model->parent, new PageTransformer()) : null;
}
public function includeSections(Page $model): Collection {
return $this->collection($model->sections, new ObjectTransformer());
}
public function includeSidebars(Page $model): Collection {
return $this->collection($model->sidebars, new ObjectTransformer());
}
public function includePermissions(Page $model): Primitive {
return $this->primitive((new PermissionsService($model))->get());
}
}

View File

@ -17,6 +17,7 @@ class CreateObjectablesTable extends Migration
$table->id();
$table->integer('nir_object_id')->index()->nullable();
$table->nullableMorphs('objectable');
$table->integer('ord')->index()->default(0);
$table->timestamps();
});
}

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePagesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('pages', function (Blueprint $table) {
$table->id();
$table->char('uuid', 36)->index()->unique();
$table->integer('parent_id')->index()->default(0);
$table->string('slug')->index()->nullable();
$table->string('type')->index()->nullable();
$table->string('name')->index()->nullable();
$table->string('title')->index()->nullable();
$table->string('h1')->index()->nullable();
$table->integer('ord')->index()->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('pages');
}
}

View File

@ -4,6 +4,7 @@ namespace Database\Seeders;
use Database\Seeders\Dictionaries\DictionariesTablesSeeder;
use Database\Seeders\Objects\ObjectsTablesSeeder;
use Database\Seeders\Pages\PagesTableSeeder;
use Database\Seeders\Users\RoleTableSeeder;
use Database\Seeders\Users\UsersTableSeeder;
@ -15,5 +16,6 @@ class DatabaseSeeder extends Seeder {
$this->call(UsersTableSeeder::class);
$this->call(ObjectsTablesSeeder::class);
$this->call(DictionariesTablesSeeder::class);
$this->call(PagesTableSeeder::class);
}
}

View File

@ -7,6 +7,10 @@ use Illuminate\Database\Seeder;
class DictionariesTableSeeder extends Seeder {
public array $dictionaries = [
'list-types' => [
'title' => 'Виды списка',
'items' => ['marked' => 'Маркированный', 'numeric' => 'Нумерованный']
]
];
public function run() {

View File

@ -2,12 +2,78 @@
namespace Database\Seeders\Objects;
use App\Models\Dictionaries\DictionaryItem;
use App\Models\Objects\Field;
use App\Models\Objects\FieldType;
use App\Transformers\Dictionaries\DictionaryItemTransformer;
use Illuminate\Database\Seeder;
class FieldsTableSeeder extends Seeder {
public array $fields = [
'header' => [
'title' => 'Текст заголовка',
'type' => FieldType::TEXT
],
'header-required' => [
'title' => 'Текст заголовка',
'type' => FieldType::TEXT,
'required' => true
],
'subheader' => [
'title' => 'Текст подзаголовка',
'type' => FieldType::TEXT
],
'subheader-required' => [
'title' => 'Текст подзаголовка',
'type' => FieldType::TEXT,
'required' => true
],
'documents' => [
'title' => 'Документы',
'type' => FieldType::DOCUMENT,
'multiple' => true
],
'documents-required' => [
'title' => 'Документы',
'type' => FieldType::DOCUMENT,
'multiple' => true,
'required' => true
],
'text' => [
'title' => 'Содержимое текстового блока',
'type' => FieldType::TEXT
],
'text-required' => [
'title' => 'Содержимое текстового блока',
'type' => FieldType::TEXT,
'required' => true
],
'list-type' => [
'title' => 'Вид списка',
'type' => FieldType::RELATION,
'required' => true,
'params' => [
'related' => DictionaryItem::class, 'transformer' => DictionaryItemTransformer::class,
'options' => ['show' => true, 'whereHas' => ['dictionary' => ['name' => 'list-types']]]
]
],
'list-items' => [
'title' => 'Элементы списка',
'type' => FieldType::TEXT,
'multiple' => true,
'required' => true
],
'images' => [
'title' => 'Изображения',
'type' => FieldType::IMAGE,
'multiple' => true,
'required' => true
]
];
public function run() {

View File

@ -9,6 +9,41 @@ use Illuminate\Database\Seeder;
class ObjectTypeFieldsTableSeeder extends Seeder {
public array $objectTypeFields = [
'page-sidebar' => [
'common' => [
'fields' => ['header', 'subheader', 'documents']
]
],
'page-section-header' => [
'common' => [
'fields' => ['header-required']
]
],
'page-section-subheader' => [
'common' => [
'fields' => ['subheader-required']
]
],
'page-section-text' => [
'common' => [
'fields' => ['text-required']
]
],
'page-section-list' => [
'common' => [
'fields' => ['list-type', 'list-items']
]
],
'page-section-images' => [
'common' => [
'fields' => ['images']
]
],
'page-section-videos' => [
'common' => [
'fields' => []
]
]
];

View File

@ -7,7 +7,32 @@ use Illuminate\Database\Seeder;
class ObjectTypesTableSeeder extends Seeder {
public array $types = [
'page-sidebar' => [
'title' => 'Сторонний блок контентной страницы'
],
'page-section' => [
'title' => 'Секция контентной страницы',
'children' => [
'page-section-header' => [
'title' => 'Заголовок'
],
'page-section-subheader' => [
'title' => 'Подзаголовок'
],
'page-section-text' => [
'title' => 'Текст'
],
'page-section-list' => [
'title' => 'Список'
],
'page-section-images' => [
'title' => 'Изображения'
],
'page-section-videos' => [
'title' => 'Видео'
]
]
]
];
public function run() {

View File

@ -0,0 +1,90 @@
<?php
namespace Database\Seeders\Pages;
use App\Models\Pages\Page;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
class PagesTableSeeder extends Seeder {
public array $pages = [
'О центре' => [
'children' => [
'Руководство' => [
],
'Документы' => [
'children' => [
'Документы об учреждении' => [],
'Закупки' => [],
'Бухгалтерская отчетность' => [],
'Государственное задание' => [],
'Нормативные правовые акты' => [],
'Антимонопольное законодательство' => [],
'Специальная оценка условий труда' => []
]
],
'Структура' => [
],
'Наблюдательный совет' => [
'children' => [
'Структура' => [],
'Документы' => [],
'Решения' => []
]
],
'Закупки' => [
],
'Противодействие коррупции' => [
'children' => [
'ФЗ, указы, постановления' => [],
'Ведомственные нормативные правовые акты' => [],
'Внутренние нормативные документы' => [],
'Антикоррупционная экспертиза' => [],
'Методические материалы' => [],
'Формы документов для заполнения' => [],
'Финансовые сведения' => [],
'Aттестационная комиссия' => [],
'Обратная связь' => [],
'Остальные документы' => []
]
]
]
],
'Деятельность' => [
],
'Коммерческие услуги' => [
],
'Пресс-центр' => [
],
'Контакты' => [
]
];
public function run() {
$ord = 0;
collect($this->pages)->each(function($data, $name) use(&$ord) {
$data['ord'] = $ord++;
$this->importPage($name, $data);
});
}
public function importPage($name, $data, ?Page $parent = null) {
$slug = Str::slug(Str::transliterate($name));
$page = Page::firstOrCreate(['parent_id' => $parent->id ?? 0, 'slug' => $slug]);
$page->update(['name' => $name]);
if ($v = collect($data)->except('children')->all()) $page->update($v);
$ord = 0;
collect($data['children'] ?? [])->each(function($data, $name) use($page, &$ord) {
$data['ord'] = $ord++;
$this->importPage($name, $data, $page);
});
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Database\Seeders\Pages;
use Illuminate\Database\Seeder;
class PagesTablesSeeder extends Seeder {
public function run() {
$this->call(PagesTableSeeder::class);
}
}

View File

@ -14,6 +14,10 @@ Route::put('passwords/reset', 'Api\Auth\PasswordsController@update');
Route::get('/check-email', 'Api\Auth\RegisterController@checkEmail');
Route::get('pages', 'Api\Pages\PagesController@index');
Route::get('pages/root', 'Api\Pages\PagesController@root');
Route::get('pages/{id}', 'Api\Pages\PagesController@show');
Route::group(['middleware' => ['auth:api']], function() {
Route::apiResource('users', 'Api\Users\UsersController');
Route::apiResource('roles', 'Api\Users\RolesController');