step 1, 2 and 3 of the implementation plan

This commit is contained in:
2026-02-03 09:43:23 +01:00
parent d38001e3e2
commit 3684d9ef6b
34 changed files with 4070 additions and 18 deletions

48
app/Models/Answer.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class Answer extends Model
{
/**
* Fillable attributes for mass assignment.
*/
protected $fillable = [
'session_id',
'question_id',
'value',
'text_value',
];
/**
* Cast attributes to specific types.
*/
protected function casts(): array
{
return [
'session_id' => 'integer',
'question_id' => 'integer',
];
}
/**
* Get the session that owns this answer.
*/
public function session(): BelongsTo
{
return $this->belongsTo(Session::class);
}
/**
* Get the question that this answer belongs to.
*/
public function question(): BelongsTo
{
return $this->belongsTo(Question::class);
}
}

45
app/Models/Category.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
final class Category extends Model
{
/**
* Fillable attributes for mass assignment.
*/
protected $fillable = [
'name',
'sort_order',
];
/**
* Cast attributes to specific types.
*/
protected function casts(): array
{
return [
'sort_order' => 'integer',
];
}
/**
* Get all question groups for this category.
*/
public function questionGroups(): HasMany
{
return $this->hasMany(QuestionGroup::class);
}
/**
* Get all sessions for this category.
*/
public function sessions(): HasMany
{
return $this->hasMany(Session::class);
}
}

78
app/Models/Log.php Normal file
View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class Log extends Model
{
/**
* Disable the updated_at timestamp for append-only logs.
*/
public const UPDATED_AT = null;
/**
* Fillable attributes for mass assignment.
*/
protected $fillable = [
'user_id',
'session_id',
'category_id',
'action',
'metadata',
];
/**
* Cast attributes to specific types.
*/
protected function casts(): array
{
return [
'user_id' => 'integer',
'session_id' => 'integer',
'category_id' => 'integer',
'metadata' => 'array',
];
}
/**
* Boot the model and prevent updates and deletes.
*/
protected static function booted(): void
{
self::updating(function (): bool {
return false;
});
self::deleting(function (): bool {
return false;
});
}
/**
* Get the user that performed the logged action.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Get the session associated with this log entry.
*/
public function session(): BelongsTo
{
return $this->belongsTo(Session::class);
}
/**
* Get the category associated with this log entry.
*/
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
}

48
app/Models/Question.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class Question extends Model
{
/**
* Fillable attributes for mass assignment.
*/
protected $fillable = [
'question_group_id',
'text',
'has_yes',
'has_no',
'has_na',
'details',
'sort_order',
'is_scored',
];
/**
* Cast attributes to specific types.
*/
protected function casts(): array
{
return [
'question_group_id' => 'integer',
'has_yes' => 'boolean',
'has_no' => 'boolean',
'has_na' => 'boolean',
'sort_order' => 'integer',
'is_scored' => 'boolean',
];
}
/**
* Get the question group that owns this question.
*/
public function questionGroup(): BelongsTo
{
return $this->belongsTo(QuestionGroup::class);
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
final class QuestionGroup extends Model
{
/**
* Fillable attributes for mass assignment.
*/
protected $fillable = [
'category_id',
'name',
'sort_order',
'description',
'scoring_instructions',
];
/**
* Cast attributes to specific types.
*/
protected function casts(): array
{
return [
'category_id' => 'integer',
'sort_order' => 'integer',
];
}
/**
* Get the category that owns this question group.
*/
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
/**
* Get all questions for this question group.
*/
public function questions(): HasMany
{
return $this->hasMany(Question::class);
}
}

57
app/Models/Screening.php Normal file
View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
final class Screening extends Model
{
/**
* Fillable attributes for mass assignment.
*/
protected $fillable = [
'user_id',
'score',
'passed',
];
/**
* Cast attributes to specific types.
*/
protected function casts(): array
{
return [
'user_id' => 'integer',
'score' => 'integer',
'passed' => 'boolean',
];
}
/**
* Get the user that owns this screening.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Get all answers for this screening.
*/
public function answers(): HasMany
{
return $this->hasMany(ScreeningAnswer::class);
}
/**
* Get all sessions that reference this screening.
*/
public function sessions(): HasMany
{
return $this->hasMany(Session::class);
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class ScreeningAnswer extends Model
{
/**
* Fillable attributes for mass assignment.
*/
protected $fillable = [
'screening_id',
'question_number',
'value',
];
/**
* Cast attributes to specific types.
*/
protected function casts(): array
{
return [
'screening_id' => 'integer',
'question_number' => 'integer',
];
}
/**
* Get the screening that owns this answer.
*/
public function screening(): BelongsTo
{
return $this->belongsTo(Screening::class);
}
}

84
app/Models/Session.php Normal file
View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
final class Session extends Model
{
protected $table = 'questionnaire_sessions';
/**
* Fillable attributes for mass assignment.
*/
protected $fillable = [
'user_id',
'category_id',
'screening_id',
'status',
'score',
'result',
'basic_info',
'additional_comments',
'completed_at',
];
/**
* Cast attributes to specific types.
*/
protected function casts(): array
{
return [
'user_id' => 'integer',
'category_id' => 'integer',
'screening_id' => 'integer',
'score' => 'integer',
'basic_info' => 'array',
'completed_at' => 'datetime',
];
}
/**
* Get the user that owns this session.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Get the category for this session.
*/
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
/**
* Get the screening that preceded this session.
*/
public function screening(): BelongsTo
{
return $this->belongsTo(Screening::class);
}
/**
* Get all answers for this session.
*/
public function answers(): HasMany
{
return $this->hasMany(Answer::class);
}
/**
* Get all logs for this session.
*/
public function logs(): HasMany
{
return $this->hasMany(Log::class);
}
}

View File

@@ -1,17 +1,18 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
class User extends Authenticatable implements MustVerifyEmail
final class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable, TwoFactorAuthenticatable;
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
@@ -46,4 +47,28 @@ protected function casts(): array
'password' => 'hashed',
];
}
/**
* Get all sessions for this user.
*/
public function sessions(): HasMany
{
return $this->hasMany(Session::class);
}
/**
* Get all screenings for this user.
*/
public function screenings(): HasMany
{
return $this->hasMany(Screening::class);
}
/**
* Get all logs for this user.
*/
public function logs(): HasMany
{
return $this->hasMany(Log::class);
}
}