105 خطوط
4.1 KiB
PHP
105 خطوط
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Modules\Taxonomy\Http;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Modules\Taxonomy\Application\ContentMappingService;
|
|
use App\Modules\Taxonomy\Domain\ContentTaxonomyMapping;
|
|
use App\Modules\Taxonomy\Domain\Enums\MappableType;
|
|
use App\Modules\Taxonomy\Domain\Enums\MappingSource;
|
|
use App\Modules\Taxonomy\Domain\Enums\MappingType;
|
|
use App\Modules\Taxonomy\Http\Requests\StoreContentMappingRequest;
|
|
use App\Modules\Taxonomy\Policies\TaxonomyPolicy;
|
|
use App\Modules\Tenancy\Application\TenantContext;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
final class ContentMappingController extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly TenantContext $tenant,
|
|
private readonly TaxonomyPolicy $policy,
|
|
private readonly ContentMappingService $service,
|
|
) {}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
abort_unless($this->policy->view($request->user()), 403);
|
|
$request->validate([
|
|
'mappableType' => ['required', Rule::enum(MappableType::class)],
|
|
'mappableId' => ['required', 'string'],
|
|
]);
|
|
|
|
$mappings = ContentTaxonomyMapping::query()
|
|
->with('taxonomyNode.type')
|
|
->where('organization_id', $this->tenant->id())
|
|
->where('mappable_type', $request->string('mappableType'))
|
|
->where('mappable_id', $request->string('mappableId'))
|
|
->get()
|
|
->map(fn (ContentTaxonomyMapping $mapping) => $this->payload($mapping));
|
|
|
|
return response()->json(['data' => $mappings]);
|
|
}
|
|
|
|
public function store(StoreContentMappingRequest $request): JsonResponse
|
|
{
|
|
abort_unless($this->policy->manage($request->user()), 403);
|
|
$data = $request->validated();
|
|
$mapping = $this->service->create(
|
|
$this->tenant->id(),
|
|
$data['courseVersionId'],
|
|
MappableType::from($data['mappableType']),
|
|
$data['mappableId'],
|
|
$data['taxonomyNodeId'],
|
|
MappingType::from($data['mappingType']),
|
|
$data['masteryLevel'] ?? null,
|
|
(float) $data['weight'],
|
|
MappingSource::from($data['source']),
|
|
isset($data['confidence']) ? (float) $data['confidence'] : null,
|
|
$request->user(),
|
|
);
|
|
|
|
return response()->json(['data' => $this->payload($mapping->load('taxonomyNode.type'))], 201);
|
|
}
|
|
|
|
public function destroy(Request $request, string $mapping): JsonResponse
|
|
{
|
|
abort_unless($this->policy->manage($request->user()), 403);
|
|
$model = ContentTaxonomyMapping::query()
|
|
->where('organization_id', $this->tenant->id())
|
|
->findOrFail($mapping);
|
|
if ($model->courseVersion->status->value === 'published') {
|
|
throw ValidationException::withMessages(['courseVersionId' => ['Published course version mappings are immutable.']]);
|
|
}
|
|
$model->delete();
|
|
|
|
return response()->json(status: 204);
|
|
}
|
|
|
|
private function payload(ContentTaxonomyMapping $mapping): array
|
|
{
|
|
return [
|
|
'id' => $mapping->getKey(),
|
|
'courseVersionId' => $mapping->course_version_id,
|
|
'entityType' => $mapping->mappable_type->value,
|
|
'entityId' => $mapping->mappable_id,
|
|
'mappingType' => $mapping->mapping_type->value,
|
|
'masteryLevel' => $mapping->mastery_level,
|
|
'weight' => (float) $mapping->weight,
|
|
'source' => $mapping->source->value,
|
|
'confidence' => $mapping->confidence === null ? null : (float) $mapping->confidence,
|
|
'confirmationStatus' => $mapping->confirmation_status->value,
|
|
'direct' => true,
|
|
'inheritedFrom' => null,
|
|
'taxonomyNode' => [
|
|
'id' => $mapping->taxonomyNode->getKey(),
|
|
'name' => $mapping->taxonomyNode->name,
|
|
'code' => $mapping->taxonomyNode->code,
|
|
'type' => $mapping->taxonomyNode->type->key,
|
|
],
|
|
];
|
|
}
|
|
}
|