Merge branch 'changeDocumentModels' into 'develop'

feat: ✨ Document->exercice nullable / Add slug in Tags and Categories...

See merge request ldc-web/projets/ldc!95
This commit is contained in:
karl.muller 2025-12-18 13:38:09 +00:00
commit b02380b655
16 changed files with 118 additions and 86 deletions

View file

@ -24,7 +24,8 @@ class StoreDocumentCategoryRequest extends FormRequest
public function rules(): array
{
return [
'name' => 'required|string|between:3,255'
'name' => 'required|string|between:3,255',
'slug' => 'required|string|between:3,255'
];
}
}

View file

@ -24,7 +24,8 @@ class StoreDocumentTagRequest extends FormRequest
public function rules(): array
{
return [
'name' => 'required|string|between:3,255'
'name' => 'required|string|between:3,255',
'slug' => 'required|string|between:3,255'
];
}
}

View file

@ -9,7 +9,6 @@ use App\Traits\Models\HasUwilImages;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
/**
@ -64,7 +63,7 @@ class Company extends Model
protected $casts = [
'published' => 'boolean',
];
/**
* Relations should be included in the cache.
*
@ -95,4 +94,4 @@ class Company extends Model
{
return $this->belongsTo(CompanySector::class, 'sector_id');
}
}
}

View file

@ -188,7 +188,7 @@ class Document extends Model implements TranslatableModel
/**
* Get the url to access a specific resource.
*
*
* @param integer $id Id field of the target model.
* @return string
*/
@ -210,7 +210,13 @@ class Document extends Model implements TranslatableModel
*/
public function tags(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(DocumentTag::class);
return $this->morphedByMany(
DocumentTag::class,
'documentable',
'documentables',
'document_id',
'documentable_id'
)->withTimestamps();
}
/**
@ -220,7 +226,13 @@ class Document extends Model implements TranslatableModel
*/
public function categories(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(DocumentCategory::class);
return $this->morphedByMany(
DocumentCategory::class,
'documentable',
'documentables',
'document_id',
'documentable_id'
)->withTimestamps();
}
public function inertiaFill(array $attributes)

View file

@ -24,6 +24,7 @@ class DocumentCategory extends Model implements TranslatableModel
*/
protected $fillable = [
'name',
'slug'
];
/**
@ -33,6 +34,7 @@ class DocumentCategory extends Model implements TranslatableModel
*/
public $translatable = [
'name',
'slug'
];
/**
@ -42,6 +44,12 @@ class DocumentCategory extends Model implements TranslatableModel
*/
public function documents(): BelongsToMany
{
return $this->belongsToMany(Document::class);
return $this->morphToMany(
Document::class,
'documentable',
'documentables',
'documentable_id',
'document_id'
);
}
}

View file

@ -24,6 +24,7 @@ class DocumentTag extends Model implements TranslatableModel
*/
protected $fillable = [
'name',
'slug'
];
/**
@ -33,6 +34,7 @@ class DocumentTag extends Model implements TranslatableModel
*/
public $translatable = [
'name',
'slug'
];
/**
@ -42,6 +44,12 @@ class DocumentTag extends Model implements TranslatableModel
*/
public function documents(): BelongsToMany
{
return $this->belongsToMany(Document::class);
return $this->morphToMany(
Document::class,
'documentable',
'documentables',
'documentable_id',
'document_id'
);
}
}

View file

@ -14,6 +14,7 @@ return new class extends Migration
Schema::create('document_categories', function (Blueprint $table) {
$table->id();
$table->text('name')->comment('Name of the category');
$table->text('slug')->comment('Slug of the category');
$table->timestamps();
});
}

View file

@ -14,6 +14,7 @@ return new class extends Migration
Schema::create('document_tags', function (Blueprint $table) {
$table->id();
$table->text('name')->comment('Name of the tag');
$table->text('slug')->comment('Slug of the tag');
$table->timestamps();
});
}

View file

@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::create('document_document_category', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('document_id');
$table->unsignedBigInteger('document_category_id');
$table->foreign('document_id', 'dcfd_document_fk')
->references('id')->on('documents')
->onDelete('cascade');
$table->foreign('document_category_id', 'dcfd_document_category_fk')
->references('id')->on('document_categories')
->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('document_document_category');
}
};

View file

@ -11,17 +11,18 @@ return new class extends Migration
*/
public function up()
{
Schema::create('document_document_tag', function (Blueprint $table) {
Schema::create('documentables', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('document_id');
$table->unsignedBigInteger('document_tag_id');
$table->foreign('document_id', 'd_pivot_d_fk')
$table->unsignedBigInteger('documentable_id');
$table->string('documentable_type');
$table->foreign('document_id')
->references('id')->on('documents')
->onDelete('cascade');
$table->foreign('document_tag_id', 'd_pivot_tag_fk')
->references('id')->on('document_tags')
->onDelete('cascade');
$table->timestamps();
$table->index(['documentable_id', 'documentable_type']);
});
}
@ -30,6 +31,6 @@ return new class extends Migration
*/
public function down(): void
{
Schema::dropIfExists('document_document_tag');
Schema::dropIfExists('documentables');
}
};

View file

@ -38,7 +38,7 @@ export default function Form({
const categories = usePage().props.categories as DocumentCategoryModel[];
const { options, defaultValue } = mapPivotOptions(tags, model?.tags, "name", locale);
const { options: categoriesOptions, defaultValue: categoryDefaultValue } = mapPivotOptions(categories, model?.categories, "name", locale);
console.log(model)
/** Form section */
const { post, setData, data, reset } = useForm({
_method: method === Methods.POST ? 'post' : 'put',
@ -47,7 +47,7 @@ export default function Form({
status: model?.status ?? enumDocStatus.unpublished.value,
path: model?.path ? model?.path[locale] : "",
tags: model?.tags ? defaultValue : [],
categories: model?.categories ? defaultValue : [],
categories: model?.categories ? categoryDefaultValue : [],
published_at: model?.published_at ? model?.published_at : null,
unpublished_at: model?.unpublished_at ? model?.unpublished_at : null
});
@ -228,6 +228,7 @@ export default function Form({
choice: 1
}),
).ucFirst().value()}
required={false}
placeholder={str(
useTrans("back.document.exercice.placeholder")
).ucFirst().value()}

View file

@ -11,6 +11,7 @@ import useTrans from "@/hooks/use-translations";
import str from "@/hooks/use-string";
import { useCrudSubmit } from "@/hooks/use-crud-submit";
import SubmitButton from "@/back/Components/SubmitButton";
import Slugifycator from "@/back/Components/Inputs/Slugifycator";
export default function Form({
model,
@ -31,7 +32,8 @@ export default function Form({
/** Form section */
const { post, setData, data } = useForm({
_method: method === Methods.POST ? 'post' : 'put',
name: model?.name ? model?.name[locale] : ""
name: model?.name ? model?.name[locale] : "",
slug: model?.slug ? model?.slug[locale] : ""
});
const submit = useCrudSubmit({
@ -51,21 +53,28 @@ export default function Form({
icon={<BookTextIcon className="mr-1" />}
description={useTrans("back.form_subtitle_global")}
>
<SimpleString
name="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.attributes.name"),
},
choice: 1
}),
).ucFirst().value()}
required={true}
placeholder="ex: John Doe"
value={data.name ?? ""}
setData={setData}
/>
<div className="grid md:grid-cols-2 grid-col-1 gap-2">
<SimpleString
name="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.attributes.name"),
},
choice: 1
}),
).ucFirst().value()}
required={true}
placeholder="ex: John Doe"
value={data.name ?? ""}
setData={setData}
/>
<Slugifycator
value={data.slug}
targetValue={data.name}
setData={setData}
/>
</div>
</FieldsetWrapper>
<div className="text-center">
<SubmitButton modelTrans={

View file

@ -43,6 +43,13 @@ export default function Show({ data, modelName, documents }: { data: SingleResou
? value[appEnv.locale]
: (<p className="text-muted-foreground italic m-0">{useTrans('back.table_not_known')}</p>),
},
{
accessorKey: "slug",
header: str(useTrans("validation.custom.slug")).ucFirst().value(),
cell: ({ value }: { value: TranslationField }) => value[appEnv.locale]
? value[appEnv.locale]
: (<p className="text-muted-foreground italic m-0">{useTrans('back.table_not_known')}</p>),
},
{
accessorKey: "created_at",
header: str(useTrans("validation.attributes.created_at")).ucFirst().value(),

View file

@ -11,6 +11,7 @@ import useTrans from "@/hooks/use-translations";
import str from "@/hooks/use-string";
import { useCrudSubmit } from "@/hooks/use-crud-submit";
import SubmitButton from "@/back/Components/SubmitButton";
import Slugifycator from "@/back/Components/Inputs/Slugifycator";
export default function Form({
model,
@ -31,7 +32,8 @@ export default function Form({
/** Form section */
const { post, setData, data } = useForm({
_method: method === Methods.POST ? 'post' : 'put',
name: model?.name ? model?.name[locale] : ""
name: model?.name ? model?.name[locale] : "",
slug: model?.slug ? model?.slug[locale] : ""
});
const submit = useCrudSubmit({
@ -51,21 +53,28 @@ export default function Form({
icon={<BookTextIcon className="mr-1" />}
description={useTrans("back.form_subtitle_global")}
>
<SimpleString
name="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.attributes.name"),
},
choice: 1
}),
).ucFirst().value()}
required={true}
placeholder="ex: John Doe"
value={data.name ?? ""}
setData={setData}
/>
<div className="grid md:grid-cols-2 grid-col-1 gap-2">
<SimpleString
name="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.attributes.name"),
},
choice: 1
}),
).ucFirst().value()}
required={true}
placeholder="ex: John Doe"
value={data.name ?? ""}
setData={setData}
/>
<Slugifycator
value={data.slug}
targetValue={data.name}
setData={setData}
/>
</div>
</FieldsetWrapper>
<div className="text-center">
<SubmitButton modelTrans={

View file

@ -43,6 +43,13 @@ export default function Show({ data, modelName, documents }: { data: SingleResou
? value[appEnv.locale]
: (<p className="text-muted-foreground italic m-0">{useTrans('back.table_not_known')}</p>),
},
{
accessorKey: "slug",
header: str(useTrans("validation.custom.slug")).ucFirst().value(),
cell: ({ value }: { value: TranslationField }) => value[appEnv.locale]
? value[appEnv.locale]
: (<p className="text-muted-foreground italic m-0">{useTrans('back.table_not_known')}</p>),
},
{
accessorKey: "created_at",
header: str(useTrans("validation.attributes.created_at")).ucFirst().value(),

View file

@ -192,6 +192,7 @@ type DocumentModel = {
type DocumentTagModel = {
id: number;
name: TranslationField;
slug: TranslationField;
documents_count?: number;
created_at: Date;
updated_at: Date;
@ -200,6 +201,7 @@ type DocumentTagModel = {
type DocumentCategoryModel = {
id: number;
name: TranslationField;
slug: TranslationField;
documents_count?: number;
created_at: Date;
updated_at: Date;