4 Commits

Author SHA1 Message Date
eb9c8796de Add Larastan level 7 analysis
All checks were successful
Tests / Laravel tests (pull_request) Successful in 2m36s
2026-06-03 22:09:33 +02:00
53c4823b22 Merge pull request 'Fix controller cleanup issues' (#2) from bugfixes/controller-cleanup-and-personalia-fixes into main
All checks were successful
Tests / Laravel tests (push) Successful in 4m15s
Reviewed-on: #2
2026-06-03 22:02:27 +02:00
fe47b79a25 Fix controller cleanup issues
All checks were successful
Tests / Laravel tests (pull_request) Successful in 3m24s
2026-06-03 21:57:10 +02:00
27449eabf0 Merge pull request 'feat: include testing, factories, git workflow for testing.' (#1) from feature/add-php-unit-tests into main
All checks were successful
Tests / Laravel tests (push) Successful in 2m7s
Reviewed-on: #1
2026-06-03 21:27:33 +02:00
31 changed files with 357 additions and 104 deletions

View File

@@ -32,3 +32,6 @@ jobs:
- name: Run test suite - name: Run test suite
run: php artisan test run: php artisan test
- name: Run static analysis
run: composer analyse

View File

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified; use Illuminate\Auth\Events\Verified;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\EmailVerificationRequest; use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@@ -14,12 +15,18 @@ class VerifyEmailController extends Controller
*/ */
public function __invoke(EmailVerificationRequest $request): RedirectResponse public function __invoke(EmailVerificationRequest $request): RedirectResponse
{ {
if ($request->user()->hasVerifiedEmail()) { $user = $request->user();
if (! $user instanceof MustVerifyEmail) {
abort(403);
}
if ($user->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
} }
if ($request->user()->markEmailAsVerified()) { if ($user->markEmailAsVerified()) {
event(new Verified($request->user())); event(new Verified($user));
} }
return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); return redirect()->intended(route('dashboard', absolute: false).'?verified=1');

View File

@@ -4,23 +4,25 @@ namespace App\Http\Controllers;
use App\Http\Requests\EducationRequest; use App\Http\Requests\EducationRequest;
use App\Models\Education; use App\Models\Education;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\View\View;
class EducationController extends Controller class EducationController extends Controller
{ {
public function index() public function index(): View
{ {
$educations = Education::with('media')->latest()->get(); $educations = Education::with('media')->latest()->get();
return view('educations.index', compact('educations')); return view('educations.index', compact('educations'));
} }
public function create() public function create(): View
{ {
return view('educations.create'); return view('educations.create');
} }
public function store(EducationRequest $request) public function store(EducationRequest $request): RedirectResponse
{ {
$education = Education::create($request->validated()); $education = Education::create($request->validated());
@@ -29,17 +31,12 @@ class EducationController extends Controller
return redirect()->route('educations.index')->with('success', 'Opleiding toegevoegd.'); return redirect()->route('educations.index')->with('success', 'Opleiding toegevoegd.');
} }
public function show(Education $education) public function edit(Education $education): View
{
return view('educations.show', compact('education'));
}
public function edit(Education $education)
{ {
return view('educations.edit', compact('education')); return view('educations.edit', compact('education'));
} }
public function update(EducationRequest $request, Education $education) public function update(EducationRequest $request, Education $education): RedirectResponse
{ {
$education->update($request->validated()); $education->update($request->validated());
@@ -48,7 +45,7 @@ class EducationController extends Controller
return redirect()->route('educations.index')->with('success', 'Opleiding bijgewerkt.'); return redirect()->route('educations.index')->with('success', 'Opleiding bijgewerkt.');
} }
public function destroy(Education $education) public function destroy(Education $education): RedirectResponse
{ {
$education->clearMediaCollection('image'); $education->clearMediaCollection('image');
$education->delete(); $education->delete();

View File

@@ -8,11 +8,13 @@ use App\Models\Education;
use App\Models\Personalia; use App\Models\Personalia;
use App\Models\Skill; use App\Models\Skill;
use App\Models\WorkExperience; use App\Models\WorkExperience;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\View\View;
class FrontendController extends Controller class FrontendController extends Controller
{ {
public function index() public function index(): View
{ {
$skills = Skill::all()->groupBy('type'); $skills = Skill::all()->groupBy('type');
@@ -23,21 +25,20 @@ class FrontendController extends Controller
return view('welcome', compact('skills', 'personalia', 'education', 'experience')); return view('welcome', compact('skills', 'personalia', 'education', 'experience'));
} }
public function getPersonalia($id) public function getPersonalia(Personalia $personalia): JsonResponse
{ {
$item = Personalia::findOrFail($id);
NotifyTelegramAboutPersonaliaClick::dispatch( NotifyTelegramAboutPersonaliaClick::dispatch(
$item, $personalia,
request()->ip(), request()->ip(),
request()->userAgent() request()->userAgent()
); );
return response()->json([ return response()->json([
'value' => $item->value, 'value' => $personalia->value,
]); ]);
} }
public function message(Request $request) public function message(Request $request): JsonResponse
{ {
$validated = $request->validate([ $validated = $request->validate([
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',

View File

@@ -2,25 +2,26 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Personalia;
use Illuminate\Http\Request;
use App\Http\Requests\PersonaliaRequest; use App\Http\Requests\PersonaliaRequest;
use App\Models\Personalia;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class PersonaliaController extends Controller class PersonaliaController extends Controller
{ {
public function index() public function index(): View
{ {
$personalia = Personalia::all(); $personalia = Personalia::all();
return view('personalia.index', compact('personalia')); return view('personalia.index', compact('personalia'));
} }
public function create() public function create(): View
{ {
return view('personalia.create'); return view('personalia.create');
} }
public function store(PersonaliaRequest $request) public function store(PersonaliaRequest $request): RedirectResponse
{ {
$validated = $request->validated(); $validated = $request->validated();
@@ -32,13 +33,13 @@ class PersonaliaController extends Controller
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item toegevoegd.'); return redirect()->route('personalia.index')->with('success', 'Persoonlijk item toegevoegd.');
} }
public function edit(Personalia $personalium) public function edit(Personalia $personalium): View
{ {
return view('personalia.edit', ['personalia' => $personalium]); return view('personalia.edit', ['personalia' => $personalium]);
} }
public function update(Request $request, Personalia $personalium) public function update(PersonaliaRequest $request, Personalia $personalium): RedirectResponse
{ {
$validated = $request->validated(); $validated = $request->validated();
@@ -46,12 +47,13 @@ class PersonaliaController extends Controller
...$validated, ...$validated,
'hidden' => $request->boolean('hidden'), 'hidden' => $request->boolean('hidden'),
]); ]);
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item bijgewerkt.'); return redirect()->route('personalia.index')->with('success', 'Persoonlijk item bijgewerkt.');
} }
public function destroy(Personalia $personalia) public function destroy(Personalia $personalium): RedirectResponse
{ {
$personalia->delete(); $personalium->delete();
return redirect()->route('personalia.index')->with('success', 'Persoonlijk item verwijderd.'); return redirect()->route('personalia.index')->with('success', 'Persoonlijk item verwijderd.');
} }

View File

@@ -2,25 +2,26 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Skill;
use Illuminate\Http\Request;
use App\Http\Requests\SkillRequest; use App\Http\Requests\SkillRequest;
use App\Models\Skill;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class SkillController extends Controller class SkillController extends Controller
{ {
public function index() public function index(): View
{ {
$skills = Skill::latest()->get(); $skills = Skill::latest()->get();
return view('skills.index', compact('skills')); return view('skills.index', compact('skills'));
} }
public function create() public function create(): View
{ {
return view('skills.create'); return view('skills.create');
} }
public function store(SkillRequest $request) public function store(SkillRequest $request): RedirectResponse
{ {
$skill = Skill::create($request->validated()); $skill = Skill::create($request->validated());
@@ -31,17 +32,12 @@ class SkillController extends Controller
return redirect()->route('skills.index')->with('success', 'Vaardigheid toegevoegd.'); return redirect()->route('skills.index')->with('success', 'Vaardigheid toegevoegd.');
} }
public function show(Skill $skill) public function edit(Skill $skill): View
{
return view('skills.show', compact('skill'));
}
public function edit(Skill $skill)
{ {
return view('skills.edit', compact('skill')); return view('skills.edit', compact('skill'));
} }
public function update(SkillRequest $request, Skill $skill) public function update(SkillRequest $request, Skill $skill): RedirectResponse
{ {
$skill->update($request->validated()); $skill->update($request->validated());
@@ -53,7 +49,7 @@ class SkillController extends Controller
return redirect()->route('skills.index')->with('success', 'Vaardigheid bijgewerkt.'); return redirect()->route('skills.index')->with('success', 'Vaardigheid bijgewerkt.');
} }
public function destroy(Skill $skill) public function destroy(Skill $skill): RedirectResponse
{ {
$skill->clearMediaCollection('image'); $skill->clearMediaCollection('image');
$skill->delete(); $skill->delete();

View File

@@ -2,26 +2,26 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\WorkExperience;
use Illuminate\Http\Request;
use App\Http\Requests\WorkExperienceRequest; use App\Http\Requests\WorkExperienceRequest;
use App\Models\WorkExperience;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class WorkExperienceController extends Controller class WorkExperienceController extends Controller
{ {
public function index() public function index(): View
{ {
$experiences = WorkExperience::with('media')->latest()->get(); $experiences = WorkExperience::with('media')->latest()->get();
return view('work_experiences.index', compact('experiences')); return view('work_experiences.index', compact('experiences'));
} }
public function create() public function create(): View
{ {
return view('work_experiences.create'); return view('work_experiences.create');
} }
public function store(WorkExperienceRequest $request) public function store(WorkExperienceRequest $request): RedirectResponse
{ {
$experience = WorkExperience::create($request->validated()); $experience = WorkExperience::create($request->validated());
@@ -32,17 +32,12 @@ class WorkExperienceController extends Controller
return redirect()->route('work-experiences.index')->with('success', 'Ervaring toegevoegd.'); return redirect()->route('work-experiences.index')->with('success', 'Ervaring toegevoegd.');
} }
public function show(WorkExperience $workExperience) public function edit(WorkExperience $workExperience): View
{
return view('work_experiences.show', compact('workExperience'));
}
public function edit(WorkExperience $workExperience)
{ {
return view('work_experiences.edit', compact('workExperience')); return view('work_experiences.edit', compact('workExperience'));
} }
public function update(WorkExperienceRequest $request, WorkExperience $workExperience) public function update(WorkExperienceRequest $request, WorkExperience $workExperience): RedirectResponse
{ {
$workExperience->update($request->validated()); $workExperience->update($request->validated());
@@ -54,7 +49,7 @@ class WorkExperienceController extends Controller
return redirect()->route('work-experiences.index')->with('success', 'Ervaring bijgewerkt.'); return redirect()->route('work-experiences.index')->with('success', 'Ervaring bijgewerkt.');
} }
public function destroy(WorkExperience $workExperience) public function destroy(WorkExperience $workExperience): RedirectResponse
{ {
$workExperience->clearMediaCollection('image'); $workExperience->clearMediaCollection('image');
$workExperience->delete(); $workExperience->delete();

View File

@@ -11,6 +11,9 @@ class EducationRequest extends FormRequest
return true; return true;
} }
/**
* @return array<string, list<string>>
*/
public function rules(): array public function rules(): array
{ {
return [ return [
@@ -23,6 +26,9 @@ class EducationRequest extends FormRequest
]; ];
} }
/**
* @return array<string, string>
*/
public function messages(): array public function messages(): array
{ {
return [ return [

View File

@@ -11,6 +11,9 @@ class PersonaliaRequest extends FormRequest
return true; return true;
} }
/**
* @return array<string, list<string>>
*/
public function rules(): array public function rules(): array
{ {
return [ return [
@@ -21,6 +24,9 @@ class PersonaliaRequest extends FormRequest
]; ];
} }
/**
* @return array<string, string>
*/
public function messages(): array public function messages(): array
{ {
return [ return [
@@ -28,5 +34,4 @@ class PersonaliaRequest extends FormRequest
'value.required' => 'Een waarde is verplicht.', 'value.required' => 'Een waarde is verplicht.',
]; ];
} }
} }

View File

@@ -11,6 +11,9 @@ class SkillRequest extends FormRequest
return true; return true;
} }
/**
* @return array<string, list<string>>
*/
public function rules(): array public function rules(): array
{ {
return [ return [
@@ -21,6 +24,10 @@ class SkillRequest extends FormRequest
'type' => ['required', 'in:rating,tag,other'], 'type' => ['required', 'in:rating,tag,other'],
]; ];
} }
/**
* @return array<string, string>
*/
public function messages(): array public function messages(): array
{ {
return [ return [
@@ -29,5 +36,4 @@ class SkillRequest extends FormRequest
'type.in' => 'Het type moet rating, tag of other zijn.', 'type.in' => 'Het type moet rating, tag of other zijn.',
]; ];
} }
} }

View File

@@ -11,6 +11,9 @@ class WorkExperienceRequest extends FormRequest
return true; return true;
} }
/**
* @return array<string, list<string>>
*/
public function rules(): array public function rules(): array
{ {
return [ return [
@@ -23,6 +26,9 @@ class WorkExperienceRequest extends FormRequest
]; ];
} }
/**
* @return array<string, string>
*/
public function messages(): array public function messages(): array
{ {
return [ return [

View File

@@ -21,9 +21,9 @@ class NotifyTelegramAboutContactMessage implements ShouldQueue
protected string $userAgent; protected string $userAgent;
protected string $email; protected ?string $email;
protected string $phone; protected ?string $phone;
public function __construct(string $name, string $message, string $ip, string $userAgent, ?string $email = null, ?string $phone = null) public function __construct(string $name, string $message, string $ip, string $userAgent, ?string $email = null, ?string $phone = null)
{ {
@@ -35,7 +35,7 @@ class NotifyTelegramAboutContactMessage implements ShouldQueue
$this->phone = $phone; $this->phone = $phone;
} }
public function handle() public function handle(): void
{ {
$email = $this->email ?? ''; $email = $this->email ?? '';
$phone = $this->phone ?? ''; $phone = $this->phone ?? '';

View File

@@ -14,27 +14,30 @@ class NotifyTelegramAboutPersonaliaClick implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $personalia; protected Personalia $personalia;
protected $ip; protected ?string $ip;
protected $userAgent; protected ?string $userAgent;
public function __construct(Personalia $personalia, $ip, $userAgent) public function __construct(Personalia $personalia, ?string $ip, ?string $userAgent)
{ {
$this->personalia = $personalia; $this->personalia = $personalia;
$this->ip = $ip; $this->ip = $ip;
$this->userAgent = $userAgent; $this->userAgent = $userAgent;
} }
public function handle() public function handle(): void
{ {
$ip = $this->ip ?? '';
$userAgent = $this->userAgent ?? '';
$message = <<<TEXT $message = <<<TEXT
👤 *Persoonlijke gegevens bekeken* 👤 *Persoonlijke gegevens bekeken*
Naam: {$this->personalia->value} Naam: {$this->personalia->value}
IP: {$this->ip} IP: {$ip}
User Agent: `{$this->userAgent}` User Agent: `{$userAgent}`
📅 Tijdstip: *{now()->format('d-m-Y H:i')}* 📅 Tijdstip: *{now()->format('d-m-Y H:i')}*
TEXT; TEXT;

View File

@@ -6,10 +6,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia; use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class Education extends Model implements HasMedia class Education extends Model implements HasMedia
{ {
/** @use HasFactory<\Database\Factories\EducationFactory> */
use HasFactory; use HasFactory;
use InteractsWithMedia; use InteractsWithMedia;
protected $table = 'education'; protected $table = 'education';
@@ -22,12 +25,12 @@ class Education extends Model implements HasMedia
'beschrijving', 'beschrijving',
]; ];
public function image() public function image(): ?Media
{ {
return $this->getFirstMedia('image'); return $this->getFirstMedia('image');
} }
public function imageUrl() public function imageUrl(): ?string
{ {
return $this->image() ? $this->image()->getUrl() : null; return $this->image() ? $this->image()->getUrl() : null;
} }

View File

@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Model;
class Personalia extends Model class Personalia extends Model
{ {
/** @use HasFactory<\Database\Factories\PersonaliaFactory> */
use HasFactory; use HasFactory;
protected $fillable = ['key', 'value', 'hidden', 'icon']; protected $fillable = ['key', 'value', 'hidden', 'icon'];
@@ -16,14 +17,4 @@ class Personalia extends Model
protected $casts = [ protected $casts = [
'hidden' => 'boolean', 'hidden' => 'boolean',
]; ];
public function image()
{
return $this->getFirstMedia('image');
}
public function imageUrl()
{
return $this->image() ? $this->image()->getUrl() : null;
}
} }

View File

@@ -6,20 +6,23 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia; use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class Skill extends Model implements HasMedia class Skill extends Model implements HasMedia
{ {
/** @use HasFactory<\Database\Factories\SkillFactory> */
use HasFactory; use HasFactory;
use InteractsWithMedia; use InteractsWithMedia;
protected $fillable = ['title', 'description', 'rating', 'type']; protected $fillable = ['title', 'description', 'rating', 'type'];
public function image() public function image(): ?Media
{ {
return $this->getFirstMedia('image'); return $this->getFirstMedia('image');
} }
public function imageUrl() public function imageUrl(): ?string
{ {
return $this->image() ? $this->image()->getUrl() : null; return $this->image() ? $this->image()->getUrl() : null;
} }

View File

@@ -2,11 +2,12 @@
namespace App\Models; namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
class User extends Authenticatable class User extends Authenticatable implements MustVerifyEmail
{ {
/** @use HasFactory<\Database\Factories\UserFactory> */ /** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable; use HasFactory, Notifiable;

View File

@@ -6,10 +6,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia; use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class WorkExperience extends Model implements HasMedia class WorkExperience extends Model implements HasMedia
{ {
/** @use HasFactory<\Database\Factories\WorkExperienceFactory> */
use HasFactory; use HasFactory;
use InteractsWithMedia; use InteractsWithMedia;
protected $fillable = [ protected $fillable = [
@@ -20,12 +23,12 @@ class WorkExperience extends Model implements HasMedia
'beschrijving', 'beschrijving',
]; ];
public function image() public function image(): ?Media
{ {
return $this->getFirstMedia('image'); return $this->getFirstMedia('image');
} }
public function imageUrl() public function imageUrl(): ?string
{ {
return $this->image() ? $this->image()->getUrl() : null; return $this->image() ? $this->image()->getUrl() : null;
} }

View File

@@ -13,6 +13,7 @@
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.23", "fakerphp/faker": "^1.23",
"larastan/larastan": "^3.10",
"laravel/breeze": "^2.3", "laravel/breeze": "^2.3",
"laravel/pail": "^1.2.2", "laravel/pail": "^1.2.2",
"laravel/pint": "^1.13", "laravel/pint": "^1.13",
@@ -57,6 +58,9 @@
"test": [ "test": [
"@php artisan config:clear --ansi", "@php artisan config:clear --ansi",
"@php artisan test" "@php artisan test"
],
"analyse": [
"phpstan analyse --memory-limit=1G"
] ]
}, },
"extra": { "extra": {

199
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "0d27ba8c7d653ba6d0cbd49b2eec5d2d", "content-hash": "2f5e7cf541f786348723b475b25a79c5",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@@ -6685,6 +6685,47 @@
}, },
"time": "2025-04-30T06:54:44+00:00" "time": "2025-04-30T06:54:44+00:00"
}, },
{
"name": "iamcal/sql-parser",
"version": "v0.7",
"source": {
"type": "git",
"url": "https://github.com/iamcal/SQLParser.git",
"reference": "610392f38de49a44dab08dc1659960a29874c4b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/iamcal/SQLParser/zipball/610392f38de49a44dab08dc1659960a29874c4b8",
"reference": "610392f38de49a44dab08dc1659960a29874c4b8",
"shasum": ""
},
"require-dev": {
"php-coveralls/php-coveralls": "^1.0",
"phpunit/phpunit": "^5|^6|^7|^8|^9"
},
"type": "library",
"autoload": {
"psr-4": {
"iamcal\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cal Henderson",
"email": "cal@iamcal.com"
}
],
"description": "MySQL schema parser",
"support": {
"issues": "https://github.com/iamcal/SQLParser/issues",
"source": "https://github.com/iamcal/SQLParser/tree/v0.7"
},
"time": "2026-01-28T22:20:33+00:00"
},
{ {
"name": "jean85/pretty-package-versions", "name": "jean85/pretty-package-versions",
"version": "2.1.1", "version": "2.1.1",
@@ -6745,6 +6786,96 @@
}, },
"time": "2025-03-19T14:43:43+00:00" "time": "2025-03-19T14:43:43+00:00"
}, },
{
"name": "larastan/larastan",
"version": "v3.10.0",
"source": {
"type": "git",
"url": "https://github.com/larastan/larastan.git",
"reference": "2970f83398154178a739609c244577267c7ee8eb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/larastan/larastan/zipball/2970f83398154178a739609c244577267c7ee8eb",
"reference": "2970f83398154178a739609c244577267c7ee8eb",
"shasum": ""
},
"require": {
"ext-json": "*",
"iamcal/sql-parser": "^0.7.0",
"illuminate/console": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/container": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/contracts": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/database": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/http": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/pipeline": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/support": "^11.44.2 || ^12.4.1 || ^13",
"php": "^8.2",
"phpstan/phpstan": "^2.2.0"
},
"require-dev": {
"doctrine/coding-standard": "^14",
"laravel/framework": "^11.44.2 || ^12.7.2 || ^13",
"mockery/mockery": "^1.6.12",
"nikic/php-parser": "^5.4",
"orchestra/canvas": "^v9.2.2 || ^10.0.1 || ^11",
"orchestra/testbench-core": "^9.12.0 || ^10.1 || ^11",
"phpstan/phpstan-deprecation-rules": "^2.0.1",
"phpunit/phpunit": "^10.5.35 || ^11.5.15 || ^12.5.8 || ^13.1.8"
},
"suggest": {
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
"phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically"
},
"type": "phpstan-extension",
"extra": {
"phpstan": {
"includes": [
"extension.neon"
]
},
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Larastan\\Larastan\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Can Vural",
"email": "can9119@gmail.com"
}
],
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel",
"keywords": [
"PHPStan",
"code analyse",
"code analysis",
"larastan",
"laravel",
"package",
"php",
"static analysis"
],
"support": {
"issues": "https://github.com/larastan/larastan/issues",
"source": "https://github.com/larastan/larastan/tree/v3.10.0"
},
"funding": [
{
"url": "https://github.com/canvural",
"type": "github"
}
],
"time": "2026-05-28T08:00:58+00:00"
},
{ {
"name": "laravel/breeze", "name": "laravel/breeze",
"version": "v2.3.7", "version": "v2.3.7",
@@ -7994,6 +8125,70 @@
}, },
"time": "2025-02-19T13:28:12+00:00" "time": "2025-02-19T13:28:12+00:00"
}, },
{
"name": "phpstan/phpstan",
"version": "2.2.1",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dea9c8f2d25cc849391042b71e429c1a4bf82660",
"reference": "dea9c8f2d25cc849391042b71e429c1a4bf82660",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ondřej Mirtes"
},
{
"name": "Markus Staab"
},
{
"name": "Vincent Langlet"
}
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
}
],
"time": "2026-05-28T14:44:12+00:00"
},
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "11.0.10", "version": "11.0.10",
@@ -9611,5 +9806,5 @@
"php": "^8.2" "php": "^8.2"
}, },
"platform-dev": {}, "platform-dev": {},
"plugin-api-version": "2.6.0" "plugin-api-version": "2.9.0"
} }

View File

@@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Factories\Factory;
*/ */
class EducationFactory extends Factory class EducationFactory extends Factory
{ {
/**
* @return array<string, mixed>
*/
public function definition(): array public function definition(): array
{ {
$startDate = fake()->dateTimeBetween('-8 years', '-2 years'); $startDate = fake()->dateTimeBetween('-8 years', '-2 years');

View File

@@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Factories\Factory;
*/ */
class PersonaliaFactory extends Factory class PersonaliaFactory extends Factory
{ {
/**
* @return array<string, mixed>
*/
public function definition(): array public function definition(): array
{ {
return [ return [

View File

@@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Factories\Factory;
*/ */
class SkillFactory extends Factory class SkillFactory extends Factory
{ {
/**
* @return array<string, mixed>
*/
public function definition(): array public function definition(): array
{ {
return [ return [

View File

@@ -9,6 +9,9 @@ use Illuminate\Database\Eloquent\Factories\Factory;
*/ */
class WorkExperienceFactory extends Factory class WorkExperienceFactory extends Factory
{ {
/**
* @return array<string, mixed>
*/
public function definition(): array public function definition(): array
{ {
$startDate = fake()->dateTimeBetween('-8 years', '-1 year'); $startDate = fake()->dateTimeBetween('-8 years', '-1 year');

View File

@@ -9,14 +9,14 @@ return new class extends Migration
/** /**
* Run the migrations. * Run the migrations.
*/ */
public function up() public function up(): void
{ {
Schema::table('skills', function (Blueprint $table) { Schema::table('skills', function (Blueprint $table) {
$table->string('type')->default('rating')->after('id'); $table->string('type')->default('rating')->after('id');
}); });
} }
public function down() public function down(): void
{ {
Schema::table('skills', function (Blueprint $table) { Schema::table('skills', function (Blueprint $table) {
$table->dropColumn('type'); $table->dropColumn('type');

11
phpstan.neon Normal file
View File

@@ -0,0 +1,11 @@
includes:
- vendor/larastan/larastan/extension.neon
parameters:
level: 7
paths:
- app
- database
- routes
tmpDir: storage/framework/phpstan

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" id="svg2" xml:space="preserve" viewBox="0 0 406.66666 473.33334" sodipodi:docname="Beeldmerk.ai"><metadata id="metadata8"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs id="defs6"/><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview4"/><g id="g10" inkscape:groupmode="layer" inkscape:label="Beeldmerk" transform="matrix(1.3333333,0,0,-1.3333333,0,473.33333)"><g id="g12" transform="translate(192.9405,297.8561)"><path d="M 0,0 3.661,-204.853 95.917,-260.941 95.291,47.32 Z" style="fill:#009000;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path14"/></g><g id="g16" transform="translate(287.7484,238.091)"><path d="M 0,0 -183.898,-87.583 -275.154,-31.495 1.239,108.114 Z" style="fill:#00ab00;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path18"/></g><g id="g20" transform="translate(172.7637,52.3648)"><path d="M 0,0 -93.429,-43.886 -96.399,59 -2.97,102.886 Z" style="fill:#ee0004;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path22"/></g><g id="g24" transform="translate(169.794,155.2513)"><path d="m 0,0 2.97,-102.886 -93.429,-43.887" style="fill:#d8000e;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path26"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -134,7 +134,7 @@
&copy; {{ date('Y') }} Roberto Guagliardo. Alle rechten voorbehouden. &copy; {{ date('Y') }} Roberto Guagliardo. Alle rechten voorbehouden.
</footer> </footer>
<div id="custom-cursor"> <div id="custom-cursor">
{!! file_get_contents(public_path('storage/sitiweb.svg')) !!} {!! file_get_contents(resource_path('images/sitiweb.svg')) !!}
</div> </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>

View File

@@ -8,10 +8,10 @@ use App\Http\Controllers\SkillController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
Route::get('/', [FrontendController::class, 'index'])->name('home'); Route::get('/', [FrontendController::class, 'index'])->name('home');
Route::get('/dashboard', function () { Route::get('/dashboard', function (): \Illuminate\View\View {
return view('dashboard'); return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard'); })->middleware(['auth', 'verified'])->name('dashboard');
Route::get('/getPersonalia/{id}', [FrontendController::class, 'getPersonalia'])->name('personalia'); Route::get('/getPersonalia/{personalia}', [FrontendController::class, 'getPersonalia'])->name('personalia');
Route::post('/contact', [FrontendController::class, 'message'])->name('contact'); Route::post('/contact', [FrontendController::class, 'message'])->name('contact');
Route::middleware('auth')->group(function () { Route::middleware('auth')->group(function () {
@@ -19,10 +19,10 @@ Route::middleware('auth')->group(function () {
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
Route::resource('work-experiences', \App\Http\Controllers\WorkExperienceController::class); Route::resource('work-experiences', \App\Http\Controllers\WorkExperienceController::class)->except(['show']);
Route::resource('skills', SkillController::class); Route::resource('skills', SkillController::class)->except(['show']);
Route::resource('personalia', PersonaliaController::class); Route::resource('personalia', PersonaliaController::class)->except(['show']);
Route::resource('educations', EducationController::class); Route::resource('educations', EducationController::class)->except(['show']);
}); });

View File

@@ -41,7 +41,7 @@ test('the homepage shows the public cv data', function () {
->assertViewHas('personalia', fn ($personalia) => $personalia->contains($personalium)) ->assertViewHas('personalia', fn ($personalia) => $personalia->contains($personalium))
->assertViewHas('education', fn ($educations) => $educations->contains($education)) ->assertViewHas('education', fn ($educations) => $educations->contains($education))
->assertViewHas('experience', fn ($experiences) => $experiences->contains($experience)); ->assertViewHas('experience', fn ($experiences) => $experiences->contains($experience));
})->skip('Homepage currently depends on missing public/storage/sitiweb.svg.'); });
test('a hidden personalia value can be requested and the click is queued for notification', function () { test('a hidden personalia value can be requested and the click is queued for notification', function () {
Queue::fake(); Queue::fake();

View File

@@ -88,11 +88,12 @@ test('an authenticated user can update personalia', function () {
->assertSessionHasNoErrors() ->assertSessionHasNoErrors()
->assertRedirect(route('personalia.index')); ->assertRedirect(route('personalia.index'));
expect($personalium->refresh()) $personalium->refresh();
->value->toBe('new@example.com')
->hidden->toBeFalse() expect($personalium->value)->toBe('new@example.com');
->icon->toBe('fa-regular fa-envelope'); expect($personalium->hidden)->toBeFalse();
})->skip('PersonaliaController::update currently uses Request instead of PersonaliaRequest.'); expect($personalium->icon)->toBe('fa-regular fa-envelope');
});
test('an authenticated user can delete personalia', function () { test('an authenticated user can delete personalia', function () {
$user = User::factory()->create(); $user = User::factory()->create();
@@ -104,4 +105,4 @@ test('an authenticated user can delete personalia', function () {
->assertRedirect(route('personalia.index')); ->assertRedirect(route('personalia.index'));
$this->assertDatabaseMissing('personalia', ['id' => $personalium->id]); $this->assertDatabaseMissing('personalia', ['id' => $personalium->id]);
})->skip('PersonaliaController::destroy currently does not match the resource route parameter binding.'); });