vcard4reseller/backend/src/Repository/EmployeeRepository.php
Thomas Peterson bcc06e697b Rechte: User in Employee verschmolzen (eine Identität pro Person)
Beseitigt die Doppelung Admin-Login vs. Mitarbeiter — jeder ist ein Employee
mit optionalem Login/Rechtegruppe (Voraussetzung für Mitarbeiter-Zeiterfassung).

- Employee implementiert UserInterface/PasswordAuthenticated (loginEmail unique,
  password, roles); User-Entität entfernt; Security-Provider → Employee.loginEmail
- Plattform = Reseller mit isPlatform + Org-Firma; Reseller haben Org-Firma
  (Company.isResellerOrg) für ihr Personal → alles = Reseller→Firma→Mitarbeiter
- TenantContext leitet Reseller/Company aus dem Mitarbeiter ab (Reseller-/
  Plattform-Admin = reseller-weit)
- UserAdminController: Login pro Mitarbeiter vergeben/entziehen
  (POST/DELETE /api/employees/{id}/login), /api/users = Logins-Übersicht
- Provisioning/Seed auf das neue Modell; Migrationen zu einer Baseline gesquasht
- Frontend: EmployeesView Login-Block + UsersView (Logins-Übersicht)

Verifiziert: Login, /me, Mandantenscoping, delegierter Grant (Eskalation→403),
öffentliches Profil, SPA-Flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 17:27:38 +02:00

65 lines
2.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Repository;
use App\Entity\Employee;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
/**
* @extends ServiceEntityRepository<Employee>
*/
class EmployeeRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Employee::class);
}
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
{
if (!$user instanceof Employee) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', $user::class));
}
$user->setPassword($newHashedPassword);
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
}
/**
* Lädt ein öffentlich sichtbares (aktives) Profil anhand Firmen- und
* Mitarbeiter-Slug. Nicht mandantengefiltert diese Seiten sind öffentlich.
*/
public function findPublic(string $companySlug, string $slug): ?Employee
{
return $this->createQueryBuilder('e')
->join('e.company', 'c')
->andWhere('c.slug = :companySlug')
->andWhere('e.slug = :slug')
->andWhere('e.status = :status')
->setParameter('companySlug', $companySlug)
->setParameter('slug', $slug)
->setParameter('status', 'active')
->getQuery()
->getOneOrNullResult();
}
/** Aktives Profil über den stabilen NFC/QR-Kurz-Code (für /t/{code}). */
public function findByShortCode(string $shortCode): ?Employee
{
return $this->findOneBy(['shortCode' => $shortCode, 'status' => 'active']);
}
/** @return Employee[] Mitarbeiter ohne Kurz-Code (für Backfill). */
public function findWithoutShortCode(): array
{
return $this->createQueryBuilder('e')
->andWhere('e.shortCode IS NULL')
->getQuery()
->getResult();
}
}