This commit is contained in:
2026-02-04 23:31:53 +01:00
commit cd59ccd99b
2401 changed files with 56808 additions and 0 deletions

View File

@@ -0,0 +1,307 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class DepartmentRepository
{
private static function unwrap(?array $row): ?array
{
if (!$row) {
return null;
}
return $row['departments'] ?? null;
}
private static function unwrapList($rows): array
{
if (!is_array($rows)) {
return [];
}
$list = [];
foreach ($rows as $row) {
$department = $row['departments'] ?? null;
if (is_array($department)) {
$list[] = $department;
}
}
return $list;
}
public static function list(): array
{
$rows = DB::select(
'select id, uuid, description, created_by, modified_by, created, modified from departments order by id desc'
);
return self::unwrapList($rows);
}
public static function listIds(): array
{
$rows = DB::select('select id from departments');
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['departments'] ?? $row;
if (is_array($data) && isset($data['id'])) {
$ids[] = (int) $data['id'];
}
}
return array_values(array_unique($ids));
}
public static function listByTenantIds(array $tenantIds): array
{
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
$tenantIds = array_filter($tenantIds, static fn ($id) => $id > 0);
if (!$tenantIds) {
return [];
}
$placeholders = implode(',', array_fill(0, count($tenantIds), '?'));
$rows = DB::select(
'select departments.id, departments.uuid, departments.description, departments.created_by, departments.modified_by, departments.created, departments.modified ' .
'from departments departments join tenant_departments td on td.department_id = departments.id ' .
'where td.tenant_id in (' . $placeholders . ') ' .
'group by departments.id, departments.uuid, departments.description, departments.created_by, departments.modified_by, departments.created, departments.modified ' .
'order by departments.description asc',
...array_map('strval', $tenantIds)
);
return self::unwrapList($rows);
}
public static function listByIds(array $departmentIds): array
{
$departmentIds = array_values(array_unique(array_map('intval', $departmentIds)));
$departmentIds = array_filter($departmentIds, static fn ($id) => $id > 0);
if (!$departmentIds) {
return [];
}
$placeholders = implode(',', array_fill(0, count($departmentIds), '?'));
$rows = DB::select(
'select id, uuid, description, created_by, modified_by, created, modified from departments where id in (' . $placeholders . ') order by description asc',
...array_map('strval', $departmentIds)
);
return self::unwrapList($rows);
}
public static function listPaged(array $options): array
{
$limit = (int) ($options['limit'] ?? 10);
if ($limit < 1) {
$limit = 10;
} elseif ($limit > 100) {
$limit = 100;
}
$offset = (int) ($options['offset'] ?? 0);
if ($offset < 0) {
$offset = 0;
}
$search = trim((string) ($options['search'] ?? ''));
$tenant = trim((string) ($options['tenant'] ?? ''));
$order = (string) ($options['order'] ?? 'id');
$dir = strtolower((string) ($options['dir'] ?? 'desc'));
$allowedOrder = ['id', 'uuid', 'description', 'created', 'modified'];
if (!in_array($order, $allowedOrder, true)) {
$order = 'id';
}
if (!in_array($dir, ['asc', 'desc'], true)) {
$dir = 'desc';
}
$where = [];
$params = [];
if ($search !== '') {
$like = '%' . $search . '%';
$where[] = '(description like ? or uuid like ?)';
$params[] = $like;
$params[] = $like;
}
if ($tenant !== '') {
$where[] = "exists (select 1 from tenant_departments td join tenants t on t.id = td.tenant_id and t.status = 'active' where td.department_id = departments.id and t.uuid = ?)";
$params[] = $tenant;
}
if (!empty($options['tenantUserId'])) {
$tenantUserId = (int) $options['tenantUserId'];
if ($tenantUserId > 0) {
if (defined('TENANT_SCOPE_STRICT') && TENANT_SCOPE_STRICT) {
$where[] = 'exists (select 1 from tenant_departments td ' .
"join tenants t on t.id = td.tenant_id and t.status = 'active' " .
'join user_tenants ut on ut.tenant_id = td.tenant_id ' .
'where ut.user_id = ? and td.department_id = departments.id)';
$params[] = (string) $tenantUserId;
} else {
$where[] = '(not exists (select 1 from tenant_departments td where td.department_id = departments.id) ' .
'or exists (select 1 from tenant_departments td ' .
"join tenants t on t.id = td.tenant_id and t.status = 'active' " .
'join user_tenants ut on ut.tenant_id = td.tenant_id ' .
'where ut.user_id = ? and td.department_id = departments.id))';
$params[] = (string) $tenantUserId;
}
}
} elseif (array_key_exists('tenantIds', $options)) {
$tenantIds = $options['tenantIds'];
$tenantIds = is_array($tenantIds) ? array_values(array_unique(array_map('intval', $tenantIds))) : [];
if ($tenantIds) {
if (defined('TENANT_SCOPE_STRICT') && TENANT_SCOPE_STRICT) {
$where[] = 'exists (select 1 from tenant_departments td where td.department_id = departments.id and td.tenant_id in (???))';
$params[] = $tenantIds;
} else {
$where[] = '(not exists (select 1 from tenant_departments td where td.department_id = departments.id) ' .
'or exists (select 1 from tenant_departments td where td.department_id = departments.id and td.tenant_id in (???)))';
$params[] = $tenantIds;
}
} else {
if (defined('TENANT_SCOPE_STRICT') && TENANT_SCOPE_STRICT) {
$where[] = '1=0';
} else {
$where[] = 'not exists (select 1 from tenant_departments td where td.department_id = departments.id)';
}
}
}
$whereSql = $where ? (' where ' . implode(' and ', $where)) : '';
$count = DB::selectValue('select count(*) from departments' . $whereSql, ...$params);
$total = $count ? (int) $count : 0;
$query = 'select id, uuid, description, created_by, modified_by, created, modified from departments' .
$whereSql .
sprintf(' order by `%s` %s limit ? offset ?', $order, $dir);
$queryParams = array_merge($params, [(string) $limit, (string) $offset]);
$rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams));
$list = self::unwrapList($rows);
$ids = [];
foreach ($list as $department) {
if (isset($department['id'])) {
$ids[] = (int) $department['id'];
}
}
$tenantLabelsByDepartment = [];
if ($ids) {
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$labelRows = DB::select(
"select td.department_id as department_id, t.description as description from tenant_departments td join tenants t on t.id = td.tenant_id and t.status = 'active' " .
'where td.department_id in (' . $placeholders . ') order by t.description asc',
...array_map('strval', $ids)
);
$collectLabels = static function (array $rows): array {
$labelsByDepartment = [];
foreach ($rows as $row) {
$departmentId = 0;
$label = '';
if (isset($row['td']) && is_array($row['td'])) {
$departmentId = (int) ($row['td']['department_id'] ?? 0);
}
if (isset($row['t']) && is_array($row['t'])) {
$label = (string) ($row['t']['description'] ?? '');
}
if ($departmentId === 0 || $label === '') {
foreach ($row as $value) {
if (!is_array($value)) {
continue;
}
if ($departmentId === 0 && isset($value['department_id'])) {
$departmentId = (int) $value['department_id'];
}
if ($label === '' && isset($value['description'])) {
$label = (string) $value['description'];
}
}
}
if ($departmentId === 0 && isset($row['department_id'])) {
$departmentId = (int) $row['department_id'];
}
if ($label === '' && isset($row['description'])) {
$label = (string) $row['description'];
}
if ($departmentId > 0 && $label !== '') {
$labelsByDepartment[$departmentId] ??= [];
$labelsByDepartment[$departmentId][] = $label;
}
}
return $labelsByDepartment;
};
$tenantLabelsByDepartment = $collectLabels($labelRows);
}
foreach ($list as &$department) {
$departmentId = (int) ($department['id'] ?? 0);
$department['tenant_labels'] = $tenantLabelsByDepartment[$departmentId] ?? [];
}
unset($department);
return [
'total' => $total,
'rows' => $list,
];
}
public static function find(int $id): ?array
{
$row = DB::selectOne(
'select id, uuid, description, created_by, modified_by, created, modified from departments where id = ? limit 1',
(string) $id
);
return self::unwrap($row);
}
public static function findByUuid(string $uuid): ?array
{
$row = DB::selectOne(
'select id, uuid, description, created_by, modified_by, created, modified from departments where uuid = ? limit 1',
$uuid
);
return self::unwrap($row);
}
private static function uuidV4(): string
{
$data = random_bytes(16);
$data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
$data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
public static function create(array $data)
{
return DB::insert(
'insert into departments (uuid, description, created_by, created) values (?,?,?,NOW())',
$data['uuid'] ?? self::uuidV4(),
$data['description'],
$data['created_by'] ?? null
);
}
public static function update(int $id, array $data): bool
{
$fields = [
'description' => $data['description'],
];
if (array_key_exists('modified_by', $data)) {
$fields['modified_by'] = $data['modified_by'];
}
$setParts = [];
$params = [];
foreach ($fields as $field => $value) {
$setParts[] = sprintf('`%s` = ?', $field);
$params[] = $value;
}
$params[] = (string) $id;
$query = 'update departments set ' . implode(', ', $setParts) . ' where id = ?';
$result = DB::update($query, ...$params);
return $result !== false;
}
public static function delete(int $id): bool
{
$result = DB::delete('delete from departments where id = ?', (string) $id);
return $result !== false;
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class EmailVerificationRepository
{
public static function create(int $userId, string $codeHash, string $expiresAt): ?int
{
$id = DB::insert(
'insert into email_verifications (user_id, code_hash, expires_at, attempts, used_at, created) values (?,?,?,?,NULL,NOW())',
(string) $userId,
$codeHash,
$expiresAt,
0
);
return $id ? (int) $id : null;
}
public static function invalidateForUser(int $userId): bool
{
$result = DB::update(
'update email_verifications set used_at = NOW() where user_id = ? and used_at is null',
(string) $userId
);
return $result !== false;
}
public static function findActiveByUserId(int $userId): ?array
{
$row = DB::selectOne(
'select id, user_id, code_hash, expires_at, attempts, used_at from email_verifications where user_id = ? and used_at is null and expires_at > UTC_TIMESTAMP() order by id desc limit 1',
(string) $userId
);
if (!$row || !isset($row['email_verifications'])) {
return null;
}
return $row['email_verifications'];
}
public static function findById(int $id): ?array
{
$row = DB::selectOne(
'select id, user_id, code_hash, expires_at, attempts, used_at from email_verifications where id = ? limit 1',
(string) $id
);
if (!$row || !isset($row['email_verifications'])) {
return null;
}
return $row['email_verifications'];
}
public static function incrementAttempts(int $id): bool
{
$result = DB::update(
'update email_verifications set attempts = attempts + 1 where id = ?',
(string) $id
);
return $result !== false;
}
public static function markUsed(int $id): bool
{
$result = DB::update(
'update email_verifications set used_at = NOW() where id = ?',
(string) $id
);
return $result !== false;
}
}

View File

@@ -0,0 +1,142 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class MailLogRepository
{
public static function create(array $data): ?int
{
$id = DB::insert(
'insert into mail_log (to_email, subject, template, status, created_at) values (?,?,?,?,NOW())',
$data['to_email'],
$data['subject'],
$data['template'] ?? null,
$data['status'] ?? 'queued'
);
return $id ? (int) $id : null;
}
public static function markSent(int $id, ?string $providerMessageId = null): bool
{
$result = DB::update(
'update mail_log set status = ?, sent_at = NOW(), provider_message_id = ?, error_message = NULL where id = ?',
'sent',
$providerMessageId,
(string) $id
);
return $result !== false;
}
public static function markFailed(int $id, string $errorMessage): bool
{
$result = DB::update(
'update mail_log set status = ?, error_message = ? where id = ?',
'failed',
$errorMessage,
(string) $id
);
return $result !== false;
}
private static function unwrap(?array $row): ?array
{
if (!$row) {
return null;
}
return $row['mail_log'] ?? null;
}
private static function unwrapList($rows): array
{
if (!is_array($rows)) {
return [];
}
$list = [];
foreach ($rows as $row) {
$mailLog = $row['mail_log'] ?? null;
if (is_array($mailLog)) {
$list[] = $mailLog;
}
}
return $list;
}
public static function listPaged(array $options): array
{
$limit = (int) ($options['limit'] ?? 10);
if ($limit < 1) {
$limit = 10;
} elseif ($limit > 100) {
$limit = 100;
}
$offset = (int) ($options['offset'] ?? 0);
if ($offset < 0) {
$offset = 0;
}
$search = trim((string) ($options['search'] ?? ''));
$order = (string) ($options['order'] ?? 'created_at');
$dir = strtolower((string) ($options['dir'] ?? 'desc'));
$status = trim((string) ($options['status'] ?? ''));
$createdFrom = trim((string) ($options['created_from'] ?? ''));
$createdTo = trim((string) ($options['created_to'] ?? ''));
$allowedOrder = ['id', 'created_at', 'sent_at', 'to_email', 'subject', 'status'];
if (!in_array($order, $allowedOrder, true)) {
$order = 'created_at';
}
if (!in_array($dir, ['asc', 'desc'], true)) {
$dir = 'desc';
}
$where = [];
$params = [];
if ($search !== '') {
$like = '%' . $search . '%';
$where[] = '(to_email like ? or subject like ? or template like ?)';
$params[] = $like;
$params[] = $like;
$params[] = $like;
}
if ($status !== '') {
$where[] = 'status = ?';
$params[] = $status;
}
if ($createdFrom !== '') {
$where[] = 'created_at >= ?';
$params[] = $createdFrom . ' 00:00:00';
}
if ($createdTo !== '') {
$where[] = 'created_at <= ?';
$params[] = $createdTo . ' 23:59:59';
}
$whereSql = $where ? (' where ' . implode(' and ', $where)) : '';
$count = DB::selectValue('select count(*) from mail_log' . $whereSql, ...$params);
$total = $count ? (int) $count : 0;
$query = 'select id, to_email, subject, template, status, created_at, sent_at, error_message, provider_message_id from mail_log' .
$whereSql .
sprintf(' order by `%s` %s limit ? offset ?', $order, $dir);
$queryParams = array_merge($params, [(string) $limit, (string) $offset]);
$rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams));
return [
'total' => $total,
'rows' => self::unwrapList($rows),
];
}
public static function find(int $id): ?array
{
$row = DB::selectOne(
'select id, to_email, subject, template, status, created_at, sent_at, error_message, provider_message_id from mail_log where id = ? limit 1',
(string) $id
);
return self::unwrap($row);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class PageContentRepository
{
private static function unwrap(?array $row): ?array
{
if (!$row) {
return null;
}
return $row['page_contents'] ?? $row;
}
public static function findByPageIdAndLocale(int $pageId, string $locale): ?array
{
$row = DB::selectOne(
'select id, page_id, locale, content, created_by, modified_by, created, modified from page_contents where page_id = ? and locale = ? limit 1',
(string) $pageId,
$locale
);
return self::unwrap($row);
}
public static function create(array $data)
{
return DB::insert(
'insert into page_contents (page_id, locale, content, created_by, created) values (?,?,?,?,NOW())',
(string) $data['page_id'],
$data['locale'],
$data['content'] ?? null,
$data['created_by'] ?? null
);
}
public static function update(int $id, array $data): bool
{
$fields = [
'content' => $data['content'] ?? null,
];
if (array_key_exists('modified_by', $data)) {
$fields['modified_by'] = $data['modified_by'];
}
$setParts = [];
$params = [];
foreach ($fields as $field => $value) {
$setParts[] = sprintf('`%s` = ?', $field);
$params[] = $value;
}
$params[] = (string) $id;
$query = 'update page_contents set ' . implode(', ', $setParts) . ', modified = NOW() where id = ?';
$result = DB::update($query, ...$params);
return $result !== false;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class PageRepository
{
private static function unwrap(?array $row): ?array
{
if (!$row) {
return null;
}
return $row['pages'] ?? $row;
}
public static function findBySlug(string $slug): ?array
{
$row = DB::selectOne(
'select id, uuid, slug, created_by, modified_by, created, modified from pages where slug = ? limit 1',
$slug
);
return self::unwrap($row);
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class PasswordResetRepository
{
public static function create(int $userId, string $codeHash, string $expiresAt): ?int
{
$id = DB::insert(
'insert into password_resets (user_id, code_hash, expires_at, attempts, used_at, created) values (?,?,?,?,NULL,NOW())',
(string) $userId,
$codeHash,
$expiresAt,
0
);
return $id ? (int) $id : null;
}
public static function invalidateForUser(int $userId): bool
{
$result = DB::update(
'update password_resets set used_at = NOW() where user_id = ? and used_at is null',
(string) $userId
);
return $result !== false;
}
public static function findActiveByUserId(int $userId): ?array
{
$row = DB::selectOne(
'select id, user_id, code_hash, expires_at, attempts, used_at from password_resets where user_id = ? and used_at is null and expires_at > UTC_TIMESTAMP() order by id desc limit 1',
(string) $userId
);
if (!$row || !isset($row['password_resets'])) {
return null;
}
return $row['password_resets'];
}
public static function findById(int $id): ?array
{
$row = DB::selectOne(
'select id, user_id, code_hash, expires_at, attempts, used_at from password_resets where id = ? limit 1',
(string) $id
);
if (!$row || !isset($row['password_resets'])) {
return null;
}
return $row['password_resets'];
}
public static function incrementAttempts(int $id): bool
{
$result = DB::update(
'update password_resets set attempts = attempts + 1 where id = ?',
(string) $id
);
return $result !== false;
}
public static function markUsed(int $id): bool
{
$result = DB::update(
'update password_resets set used_at = NOW() where id = ?',
(string) $id
);
return $result !== false;
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class PermissionRepository
{
public static function list(): array
{
$rows = DB::select('select id, `key`, description, created from permissions order by `key` asc');
if (!is_array($rows)) {
return [];
}
$list = [];
foreach ($rows as $row) {
$data = $row['permissions'] ?? $row;
if (is_array($data)) {
$list[] = $data;
}
}
return $list;
}
public static function listPaged(array $options): array
{
$limit = (int) ($options['limit'] ?? 10);
$offset = (int) ($options['offset'] ?? 0);
$search = trim((string) ($options['search'] ?? ''));
$order = (string) ($options['order'] ?? 'key');
$dir = strtolower((string) ($options['dir'] ?? 'asc')) === 'desc' ? 'desc' : 'asc';
$allowedOrder = ['key' => '`key`', 'description' => 'description', 'created' => 'created'];
$orderBy = $allowedOrder[$order] ?? $allowedOrder['key'];
$where = [];
$params = [];
if ($search !== '') {
$where[] = '(permissions.`key` like ? or permissions.description like ?)';
$params[] = '%' . $search . '%';
$params[] = '%' . $search . '%';
}
$whereSql = $where ? ' where ' . implode(' and ', $where) : '';
$total = DB::selectValue('select count(*) from permissions' . $whereSql, ...$params);
$query = 'select id, `key`, description, created from permissions' . $whereSql .
' order by ' . $orderBy . ' ' . $dir . ' limit ? offset ?';
$queryParams = array_merge($params, [(string) $limit, (string) $offset]);
$rows = DB::select($query, ...$queryParams);
$list = [];
foreach ($rows as $row) {
$data = $row['permissions'] ?? $row;
if (is_array($data)) {
$list[] = $data;
}
}
return ['data' => $list, 'total' => (int) $total];
}
public static function find(int $id): ?array
{
$row = DB::selectOne('select id, `key`, description, created from permissions where id = ? limit 1', (string) $id);
$data = $row['permissions'] ?? $row;
return is_array($data) ? $data : null;
}
public static function findByKey(string $key): ?array
{
$row = DB::selectOne('select id, `key`, description, created from permissions where `key` = ? limit 1', $key);
$data = $row['permissions'] ?? $row;
return is_array($data) ? $data : null;
}
public static function create(array $data): ?int
{
$key = trim((string) ($data['key'] ?? ''));
$desc = trim((string) ($data['description'] ?? ''));
$result = DB::insert(
'insert into permissions (`key`, description, created) values (?,?,NOW())',
$key,
$desc !== '' ? $desc : null
);
return $result ? (int) $result : null;
}
public static function update(int $id, array $data): bool
{
$key = trim((string) ($data['key'] ?? ''));
$desc = trim((string) ($data['description'] ?? ''));
$result = DB::update(
'update permissions set `key` = ?, description = ? where id = ?',
$key,
$desc !== '' ? $desc : null,
(string) $id
);
return $result !== false;
}
public static function delete(int $id): bool
{
$result = DB::delete('delete from permissions where id = ?', (string) $id);
return (bool) $result;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class RememberTokenRepository
{
public static function create(int $userId, string $selector, string $tokenHash, string $expiresAt): ?int
{
$id = DB::insert(
'insert into user_remember_tokens (user_id, selector, token_hash, expires_at, created) values (?,?,?,?,NOW())',
(string) $userId,
$selector,
$tokenHash,
$expiresAt
);
return $id ? (int) $id : null;
}
public static function findBySelector(string $selector): ?array
{
$row = DB::selectOne(
'select id, user_id, selector, token_hash, expires_at, last_used from user_remember_tokens where selector = ? limit 1',
$selector
);
if (!$row || !isset($row['user_remember_tokens'])) {
return null;
}
return $row['user_remember_tokens'];
}
public static function updateToken(int $id, string $tokenHash, string $expiresAt): bool
{
$result = DB::update(
'update user_remember_tokens set token_hash = ?, expires_at = ?, last_used = NOW() where id = ?',
$tokenHash,
$expiresAt,
(string) $id
);
return $result !== false;
}
public static function deleteById(int $id): bool
{
$result = DB::delete('delete from user_remember_tokens where id = ?', (string) $id);
return $result !== false;
}
public static function deleteByUserId(int $userId): bool
{
$result = DB::delete('delete from user_remember_tokens where user_id = ?', (string) $userId);
return $result !== false;
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class RolePermissionRepository
{
public static function listPermissionIdsByRoleId(int $roleId): array
{
$rows = DB::select('select permission_id from role_permissions where role_id = ?', (string) $roleId);
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['role_permissions'] ?? $row;
if (is_array($data) && isset($data['permission_id'])) {
$ids[] = (int) $data['permission_id'];
}
}
return array_values(array_unique($ids));
}
public static function replaceForRole(int $roleId, array $permissionIds): bool
{
DB::delete('delete from role_permissions where role_id = ?', (string) $roleId);
$ids = array_values(array_unique(array_filter(array_map('intval', $permissionIds))));
if (!$ids) {
return true;
}
foreach ($ids as $permissionId) {
DB::insert(
'insert into role_permissions (role_id, permission_id, created) values (?,?,NOW())',
(string) $roleId,
(string) $permissionId
);
}
return true;
}
public static function listRoleIdsByPermissionId(int $permissionId): array
{
$rows = DB::select('select role_id from role_permissions where permission_id = ?', (string) $permissionId);
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['role_permissions'] ?? $row;
if (is_array($data) && isset($data['role_id'])) {
$ids[] = (int) $data['role_id'];
}
}
return array_values(array_unique($ids));
}
public static function replaceForPermission(int $permissionId, array $roleIds): bool
{
DB::delete('delete from role_permissions where permission_id = ?', (string) $permissionId);
$ids = array_values(array_unique(array_filter(array_map('intval', $roleIds))));
if (!$ids) {
return true;
}
foreach ($ids as $roleId) {
DB::insert(
'insert into role_permissions (role_id, permission_id, created) values (?,?,NOW())',
(string) $roleId,
(string) $permissionId
);
}
return true;
}
public static function listPermissionKeysByRoleIds(array $roleIds): array
{
$ids = array_values(array_unique(array_filter(array_map('intval', $roleIds))));
if (!$ids) {
return [];
}
$rows = DB::select(
'select p.`key` as `key` from role_permissions rp join permissions p on p.id = rp.permission_id ' .
'where rp.role_id in (???)',
array_map('strval', $ids)
);
if (!is_array($rows)) {
return [];
}
$keys = [];
foreach ($rows as $row) {
$data = $row['p'] ?? $row;
if (is_array($data) && isset($data['key'])) {
$keys[] = (string) $data['key'];
} elseif (is_array($row) && isset($row['key'])) {
$keys[] = (string) $row['key'];
}
}
return array_values(array_unique($keys));
}
public static function listPermissionsWithRolesByRoleIds(array $roleIds): array
{
$ids = array_values(array_unique(array_filter(array_map('intval', $roleIds))));
if (!$ids) {
return [];
}
$rows = DB::select(
'select rp.permission_id as permission_id, p.`key` as `key`, p.description as `description`, r.description as role ' .
'from role_permissions rp join permissions p on p.id = rp.permission_id ' .
'join roles r on r.id = rp.role_id where rp.role_id in (???) ' .
'order by p.`key` asc, r.description asc',
array_map('strval', $ids)
);
if (!is_array($rows)) {
return [];
}
$permMap = [];
foreach ($rows as $row) {
$rowPerm = $row['p'] ?? ($row['permissions'] ?? $row);
$rowRole = $row['r'] ?? ($row['roles'] ?? $row);
$rowRp = $row['rp'] ?? ($row['role_permissions'] ?? $row);
$permId = $rowRp['permission_id'] ?? ($row['permission_id'] ?? null);
$key = (string) (($rowPerm['key'] ?? $row['key'] ?? '') ?? '');
if ($permId === null || $key === '') {
continue;
}
if (!isset($permMap[$permId])) {
$desc = (string) (($rowPerm['description'] ?? $row['description'] ?? '') ?? '');
$permMap[$permId] = [
'key' => $key,
'description' => $desc,
'roles' => [],
];
}
$roleLabel = (string) (($rowRole['description'] ?? $rowRole['role'] ?? $row['role'] ?? '') ?? '');
if ($roleLabel !== '') {
$permMap[$permId]['roles'][] = $roleLabel;
}
}
foreach ($permMap as &$item) {
$item['roles'] = array_values(array_unique($item['roles'] ?? []));
}
unset($item);
return array_values($permMap);
}
}

View File

@@ -0,0 +1,170 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class RoleRepository
{
private static function unwrap(?array $row): ?array
{
if (!$row) {
return null;
}
return $row['roles'] ?? null;
}
private static function unwrapList($rows): array
{
if (!is_array($rows)) {
return [];
}
$list = [];
foreach ($rows as $row) {
$role = $row['roles'] ?? null;
if (is_array($role)) {
$list[] = $role;
}
}
return $list;
}
public static function list(): array
{
$rows = DB::select(
'select id, uuid, description, created_by, modified_by, created, modified from roles order by id desc'
);
return self::unwrapList($rows);
}
public static function listIds(): array
{
$rows = DB::select('select id from roles');
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['roles'] ?? $row;
if (is_array($data) && isset($data['id'])) {
$ids[] = (int) $data['id'];
}
}
return array_values(array_unique($ids));
}
public static function listPaged(array $options): array
{
$limit = (int) ($options['limit'] ?? 10);
if ($limit < 1) {
$limit = 10;
} elseif ($limit > 100) {
$limit = 100;
}
$offset = (int) ($options['offset'] ?? 0);
if ($offset < 0) {
$offset = 0;
}
$search = trim((string) ($options['search'] ?? ''));
$order = (string) ($options['order'] ?? 'id');
$dir = strtolower((string) ($options['dir'] ?? 'desc'));
$allowedOrder = ['id', 'uuid', 'description', 'created', 'modified'];
if (!in_array($order, $allowedOrder, true)) {
$order = 'id';
}
if (!in_array($dir, ['asc', 'desc'], true)) {
$dir = 'desc';
}
$where = [];
$params = [];
if ($search !== '') {
$like = '%' . $search . '%';
$where[] = '(description like ? or uuid like ?)';
$params[] = $like;
$params[] = $like;
}
$whereSql = $where ? (' where ' . implode(' and ', $where)) : '';
$count = DB::selectValue('select count(*) from roles' . $whereSql, ...$params);
$total = $count ? (int) $count : 0;
$query = 'select id, uuid, description, created_by, modified_by, created, modified from roles' .
$whereSql .
sprintf(' order by `%s` %s limit ? offset ?', $order, $dir);
$queryParams = array_merge($params, [(string) $limit, (string) $offset]);
$rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams));
return [
'total' => $total,
'rows' => self::unwrapList($rows),
];
}
public static function find(int $id): ?array
{
$row = DB::selectOne(
'select id, uuid, description, created_by, modified_by, created, modified from roles where id = ? limit 1',
(string) $id
);
return self::unwrap($row);
}
public static function findByUuid(string $uuid): ?array
{
$row = DB::selectOne(
'select id, uuid, description, created_by, modified_by, created, modified from roles where uuid = ? limit 1',
$uuid
);
return self::unwrap($row);
}
private static function uuidV4(): string
{
$data = random_bytes(16);
$data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
$data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
public static function create(array $data)
{
return DB::insert(
'insert into roles (uuid, description, created_by, created) values (?,?,?,NOW())',
$data['uuid'] ?? self::uuidV4(),
$data['description'],
$data['created_by'] ?? null
);
}
public static function update(int $id, array $data): bool
{
$fields = [
'description' => $data['description'],
];
if (array_key_exists('modified_by', $data)) {
$fields['modified_by'] = $data['modified_by'];
}
$setParts = [];
$params = [];
foreach ($fields as $field => $value) {
$setParts[] = sprintf('`%s` = ?', $field);
$params[] = $value;
}
$params[] = (string) $id;
$query = 'update roles set ' . implode(', ', $setParts) . ' where id = ?';
$result = DB::update($query, ...$params);
return $result !== false;
}
public static function delete(int $id): bool
{
$result = DB::delete('delete from roles where id = ?', (string) $id);
return $result !== false;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class SettingRepository
{
public static function find(string $key): ?array
{
$row = DB::selectOne('select `key`, `value`, `description`, `updated_at` from settings where `key` = ? limit 1', $key);
if (!$row) {
return null;
}
return $row['settings'] ?? $row;
}
public static function getValue(string $key): ?string
{
$row = self::find($key);
if (!$row) {
return null;
}
return array_key_exists('value', $row) ? (string) $row['value'] : null;
}
public static function set(string $key, ?string $value, ?string $description = null): bool
{
$query = 'insert into settings (`key`, `value`, `description`) values (?, ?, ?) '
. 'on duplicate key update `value` = values(`value`), '
. '`description` = ifnull(values(`description`), `description`)';
$result = DB::update($query, $key, $value, $description);
return $result !== false;
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class TenantDepartmentRepository
{
public static function listTenantIdsByDepartmentId(int $departmentId): array
{
$rows = DB::select('select tenant_id from tenant_departments where department_id = ?', (string) $departmentId);
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['tenant_departments'] ?? $row;
if (is_array($data) && isset($data['tenant_id'])) {
$ids[] = (int) $data['tenant_id'];
}
}
return array_values(array_unique($ids));
}
public static function listDepartmentIdsByTenantIds(array $tenantIds): array
{
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
$tenantIds = array_filter($tenantIds, static fn ($id) => $id > 0);
if (!$tenantIds) {
return [];
}
$placeholders = implode(',', array_fill(0, count($tenantIds), '?'));
$rows = DB::select(
'select department_id from tenant_departments where tenant_id in (' . $placeholders . ')',
...array_map('strval', $tenantIds)
);
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['tenant_departments'] ?? $row;
if (is_array($data) && isset($data['department_id'])) {
$ids[] = (int) $data['department_id'];
}
}
return array_values(array_unique($ids));
}
public static function replaceForDepartment(int $departmentId, array $tenantIds): bool
{
DB::delete('delete from tenant_departments where department_id = ?', (string) $departmentId);
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
$tenantIds = array_filter($tenantIds, static fn ($id) => $id > 0);
if (!$tenantIds) {
return true;
}
foreach ($tenantIds as $tenantId) {
DB::insert(
'insert into tenant_departments (tenant_id, department_id, created) values (?,?,NOW())',
(string) $tenantId,
(string) $departmentId
);
}
return true;
}
}

View File

@@ -0,0 +1,249 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class TenantRepository
{
private static function unwrap(?array $row): ?array
{
if (!$row) {
return null;
}
return $row['tenants'] ?? null;
}
private static function unwrapList($rows): array
{
if (!is_array($rows)) {
return [];
}
$list = [];
foreach ($rows as $row) {
$tenant = $row['tenants'] ?? null;
if (is_array($tenant)) {
$list[] = $tenant;
}
}
return $list;
}
public static function list(): array
{
$rows = DB::select(
'select id, uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, modified_by, created, modified from tenants order by id desc'
);
return self::unwrapList($rows);
}
public static function listIds(): array
{
$rows = DB::select('select id from tenants');
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['tenants'] ?? $row;
if (is_array($data) && isset($data['id'])) {
$ids[] = (int) $data['id'];
}
}
return array_values(array_unique($ids));
}
public static function listActiveIdsByIds(array $tenantIds): array
{
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
if (!$tenantIds) {
return [];
}
$placeholders = implode(',', array_fill(0, count($tenantIds), '?'));
$params = array_map('strval', $tenantIds);
$rows = call_user_func_array(
['MintyPHP\\DB', 'select'],
array_merge(
["select id from tenants where status = 'active' and id in ($placeholders)"],
$params
)
);
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['tenants'] ?? $row;
if (is_array($data) && isset($data['id'])) {
$ids[] = (int) $data['id'];
}
}
return array_values(array_unique($ids));
}
public static function listPaged(array $options): array
{
$limit = (int) ($options['limit'] ?? 10);
if ($limit < 1) {
$limit = 10;
} elseif ($limit > 100) {
$limit = 100;
}
$offset = (int) ($options['offset'] ?? 0);
if ($offset < 0) {
$offset = 0;
}
$search = trim((string) ($options['search'] ?? ''));
$order = (string) ($options['order'] ?? 'id');
$dir = strtolower((string) ($options['dir'] ?? 'desc'));
$allowedOrder = ['id', 'uuid', 'description', 'created', 'modified'];
if (!in_array($order, $allowedOrder, true)) {
$order = 'id';
}
if (!in_array($dir, ['asc', 'desc'], true)) {
$dir = 'desc';
}
$where = [];
$params = [];
if ($search !== '') {
$like = '%' . $search . '%';
$where[] = '(description like ? or uuid like ?)';
$params[] = $like;
$params[] = $like;
}
if (!empty($options['tenantUserId'])) {
$tenantUserId = (int) $options['tenantUserId'];
if ($tenantUserId > 0) {
$where[] = 'exists (select 1 from user_tenants ut where ut.user_id = ? and ut.tenant_id = tenants.id)';
$params[] = (string) $tenantUserId;
}
}
$whereSql = $where ? (' where ' . implode(' and ', $where)) : '';
$count = DB::selectValue('select count(*) from tenants' . $whereSql, ...$params);
$total = $count ? (int) $count : 0;
$query = 'select id, uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, modified_by, created, modified from tenants' .
$whereSql .
sprintf(' order by `%s` %s limit ? offset ?', $order, $dir);
$queryParams = array_merge($params, [(string) $limit, (string) $offset]);
$rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams));
return [
'total' => $total,
'rows' => self::unwrapList($rows),
];
}
public static function find(int $id): ?array
{
$row = DB::selectOne(
'select id, uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, modified_by, created, modified from tenants where id = ? limit 1',
(string) $id
);
return self::unwrap($row);
}
public static function findByUuid(string $uuid): ?array
{
$row = DB::selectOne(
'select id, uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, modified_by, created, modified from tenants where uuid = ? limit 1',
$uuid
);
return self::unwrap($row);
}
private static function uuidV4(): string
{
$data = random_bytes(16);
$data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
$data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
public static function create(array $data)
{
return DB::insert(
'insert into tenants (uuid, description, address, postal_code, city, country, region, vat_id, tax_number, phone, fax, email, support_email, support_phone, billing_email, website, privacy_url, imprint_url, primary_color, status, status_changed_at, status_changed_by, created_by, created) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())',
$data['uuid'] ?? self::uuidV4(),
$data['description'],
$data['address'] ?? null,
$data['postal_code'] ?? null,
$data['city'] ?? null,
$data['country'] ?? null,
$data['region'] ?? null,
$data['vat_id'] ?? null,
$data['tax_number'] ?? null,
$data['phone'] ?? null,
$data['fax'] ?? null,
$data['email'] ?? null,
$data['support_email'] ?? null,
$data['support_phone'] ?? null,
$data['billing_email'] ?? null,
$data['website'] ?? null,
$data['privacy_url'] ?? null,
$data['imprint_url'] ?? null,
$data['primary_color'] ?? null,
$data['status'] ?? 'active',
$data['status_changed_at'] ?? null,
$data['status_changed_by'] ?? null,
$data['created_by'] ?? null
);
}
public static function update(int $id, array $data): bool
{
$fields = [
'description' => $data['description'],
'address' => $data['address'] ?? null,
'postal_code' => $data['postal_code'] ?? null,
'city' => $data['city'] ?? null,
'country' => $data['country'] ?? null,
'region' => $data['region'] ?? null,
'vat_id' => $data['vat_id'] ?? null,
'tax_number' => $data['tax_number'] ?? null,
'phone' => $data['phone'] ?? null,
'fax' => $data['fax'] ?? null,
'email' => $data['email'] ?? null,
'support_email' => $data['support_email'] ?? null,
'support_phone' => $data['support_phone'] ?? null,
'billing_email' => $data['billing_email'] ?? null,
'website' => $data['website'] ?? null,
'privacy_url' => $data['privacy_url'] ?? null,
'imprint_url' => $data['imprint_url'] ?? null,
'primary_color' => $data['primary_color'] ?? null,
'status' => $data['status'] ?? 'active',
];
if (array_key_exists('modified_by', $data)) {
$fields['modified_by'] = $data['modified_by'];
}
if (array_key_exists('status_changed_at', $data)) {
$fields['status_changed_at'] = $data['status_changed_at'];
}
if (array_key_exists('status_changed_by', $data)) {
$fields['status_changed_by'] = $data['status_changed_by'];
}
$setParts = [];
$params = [];
foreach ($fields as $field => $value) {
$setParts[] = sprintf('`%s` = ?', $field);
$params[] = $value;
}
$params[] = (string) $id;
$query = 'update tenants set ' . implode(', ', $setParts) . ' where id = ?';
$result = DB::update($query, ...$params);
return $result !== false;
}
public static function delete(int $id): bool
{
$result = DB::delete('delete from tenants where id = ?', (string) $id);
return $result !== false;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class UserDepartmentRepository
{
public static function listDepartmentIdsByUserId(int $userId): array
{
$rows = DB::select('select department_id from user_departments where user_id = ?', (string) $userId);
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['user_departments'] ?? $row;
if (is_array($data) && isset($data['department_id'])) {
$ids[] = (int) $data['department_id'];
}
}
return array_values(array_unique($ids));
}
public static function replaceForUser(int $userId, array $departmentIds): bool
{
DB::delete('delete from user_departments where user_id = ?', (string) $userId);
if (!$departmentIds) {
return true;
}
foreach ($departmentIds as $departmentId) {
DB::insert(
'insert into user_departments (user_id, department_id, created) values (?,?,NOW())',
(string) $userId,
(string) $departmentId
);
}
return true;
}
public static function removeInvalidForDepartment(int $departmentId): int
{
$query = 'delete ud from user_departments ud ' .
'where ud.department_id = ? and not exists (' .
'select 1 from user_tenants ut ' .
'join tenant_departments td on td.tenant_id = ut.tenant_id ' .
'where ut.user_id = ud.user_id and td.department_id = ud.department_id' .
')';
$result = DB::delete($query, (string) $departmentId);
return $result !== false ? (int) $result : 0;
}
}

View File

@@ -0,0 +1,534 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class UserRepository
{
private static function unwrap(?array $row): ?array
{
if (!$row) {
return null;
}
return $row['users'] ?? null;
}
private static function unwrapList($rows): array
{
if (!is_array($rows)) {
return [];
}
$list = [];
foreach ($rows as $row) {
$user = $row['users'] ?? null;
if (is_array($user)) {
foreach ($row as $key => $value) {
if ($key === 'users' || is_array($value)) {
continue;
}
$user[$key] = $value;
}
if (isset($row['pt']) && is_array($row['pt'])) {
$label = (string) ($row['pt']['description'] ?? '');
if ($label !== '') {
$user['primary_tenant_label'] = $label;
}
}
$list[] = $user;
}
}
return $list;
}
public static function list(): array
{
$rows = DB::select(
'select id, uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, theme, primary_tenant_id, created_by, modified_by, created, modified, active from users order by id desc'
);
return self::unwrapList($rows);
}
public static function listPaged(array $options): array
{
$limit = (int) ($options['limit'] ?? 10);
if ($limit < 1) {
$limit = 10;
} elseif ($limit > 100) {
$limit = 100;
}
$offset = (int) ($options['offset'] ?? 0);
if ($offset < 0) {
$offset = 0;
}
$search = trim((string) ($options['search'] ?? ''));
$active = $options['active'] ?? null;
$createdFrom = trim((string) ($options['created_from'] ?? ''));
$createdTo = trim((string) ($options['created_to'] ?? ''));
$tenant = trim((string) ($options['tenant'] ?? ''));
$tenantUuids = array_filter(array_map('trim', explode(',', (string) ($options['tenants'] ?? ''))));
$roleIds = self::normalizeIdList($options['roles'] ?? []);
$departmentIds = self::normalizeIdList($options['departments'] ?? []);
$emailVerified = $options['email_verified'] ?? null;
$order = (string) ($options['order'] ?? 'id');
$dir = strtolower((string) ($options['dir'] ?? 'desc'));
$allowedOrder = ['id', 'uuid', 'first_name', 'last_name', 'email', 'created', 'modified', 'active'];
if (!in_array($order, $allowedOrder, true)) {
$order = 'id';
}
if (!in_array($dir, ['asc', 'desc'], true)) {
$dir = 'desc';
}
$where = [];
$params = [];
if ($search !== '') {
$like = '%' . $search . '%';
$where[] = '(users.first_name like ? or users.last_name like ? or users.email like ?)';
$params[] = $like;
$params[] = $like;
$params[] = $like;
}
$activeFilter = null;
if ($active !== null && $active !== '' && $active !== 'all') {
$activeValue = strtolower((string) $active);
if (in_array($activeValue, ['1', 'true', 'active'], true)) {
$activeFilter = 1;
} elseif (in_array($activeValue, ['0', 'false', 'inactive'], true)) {
$activeFilter = 0;
}
}
if ($activeFilter !== null) {
$where[] = 'users.active = ?';
$params[] = (string) $activeFilter;
}
if ($createdFrom !== '') {
$where[] = 'users.created >= ?';
$params[] = $createdFrom . ' 00:00:00';
}
if ($createdTo !== '') {
$where[] = 'users.created <= ?';
$params[] = $createdTo . ' 23:59:59';
}
if ($tenantUuids) {
$where[] = 'exists (select 1 from user_tenants ut2 join tenants t2 on t2.id = ut2.tenant_id where ut2.user_id = users.id and t2.uuid in (???))';
$params[] = array_values($tenantUuids);
} elseif ($tenant !== '') {
$where[] = 'exists (select 1 from user_tenants ut2 join tenants t2 on t2.id = ut2.tenant_id where ut2.user_id = users.id and t2.uuid = ?)';
$params[] = $tenant;
}
if ($roleIds) {
$where[] = 'exists (select 1 from user_roles ur2 where ur2.user_id = users.id and ur2.role_id in (???))';
$params[] = array_map('strval', $roleIds);
}
if ($departmentIds) {
$where[] = 'exists (select 1 from user_departments ud2 where ud2.user_id = users.id and ud2.department_id in (???))';
$params[] = array_map('strval', $departmentIds);
}
if ($emailVerified !== null && $emailVerified !== '' && $emailVerified !== 'all') {
$emailVerifiedValue = strtolower((string) $emailVerified);
if (in_array($emailVerifiedValue, ['1', 'true', 'verified', 'yes'], true)) {
$where[] = 'users.email_verified_at is not null';
} elseif (in_array($emailVerifiedValue, ['0', 'false', 'unverified', 'no'], true)) {
$where[] = 'users.email_verified_at is null';
}
}
if (!empty($options['tenantUserId'])) {
$tenantUserId = (int) $options['tenantUserId'];
if ($tenantUserId > 0) {
if (defined('TENANT_SCOPE_STRICT') && TENANT_SCOPE_STRICT) {
$where[] = 'exists (select 1 from user_tenants ut join user_tenants ut2 on ut2.tenant_id = ut.tenant_id ' .
"join tenants t on t.id = ut.tenant_id and t.status = 'active' " .
'where ut2.user_id = ? and ut.user_id = users.id)';
$params[] = (string) $tenantUserId;
} else {
$where[] = '(not exists (select 1 from user_tenants ut where ut.user_id = users.id) ' .
'or exists (select 1 from user_tenants ut join user_tenants ut2 on ut2.tenant_id = ut.tenant_id ' .
"join tenants t on t.id = ut.tenant_id and t.status = 'active' " .
'where ut2.user_id = ? and ut.user_id = users.id))';
$params[] = (string) $tenantUserId;
}
}
}
$whereSql = $where ? (' where ' . implode(' and ', $where)) : '';
$count = DB::selectValue('select count(*) from users' . $whereSql, ...$params);
$total = $count ? (int) $count : 0;
$query = 'select users.id, users.uuid, users.first_name, users.last_name, users.email, users.profile_description, users.job_title, users.phone, users.mobile, users.short_dial, users.address, users.postal_code, users.city, users.country, users.region, users.hire_date, users.theme, users.primary_tenant_id, users.created_by, users.modified_by, users.created, users.modified, users.active, ' .
'pt.description as primary_tenant_label ' .
"from users left join tenants pt on pt.id = users.primary_tenant_id and pt.status = 'active'" .
$whereSql .
sprintf(' order by `users`.`%s` %s limit ? offset ?', $order, $dir);
$queryParams = array_merge($params, [(string) $limit, (string) $offset]);
$rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams));
$list = self::unwrapList($rows);
$labelRows = [];
$tenantMapByUser = [];
$roleLabelRows = [];
$departmentLabelRows = [];
$ids = [];
foreach ($list as $user) {
if (isset($user['id'])) {
$ids[] = (int) $user['id'];
}
}
if ($ids) {
$scopeToActiveTenants = !empty($options['tenantUserId']);
$tenantLabelJoin = $scopeToActiveTenants
? "join tenants t on t.id = ut.tenant_id and t.status = 'active' "
: 'join tenants t on t.id = ut.tenant_id ';
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$labelRows = DB::select(
'select ut.user_id as user_id, t.id as tenant_id, t.description as description from user_tenants ut ' . $tenantLabelJoin .
'where ut.user_id in (' . $placeholders . ') order by t.description asc',
...array_map('strval', $ids)
);
$roleLabelRows = DB::select(
'select ur.user_id as user_id, r.description as description from user_roles ur join roles r on r.id = ur.role_id ' .
'where ur.user_id in (' . $placeholders . ') order by r.description asc',
...array_map('strval', $ids)
);
$departmentLabelRows = DB::select(
'select ud.user_id as user_id, d.description as description from user_departments ud join departments d on d.id = ud.department_id ' .
'where ud.user_id in (' . $placeholders . ') order by d.description asc',
...array_map('strval', $ids)
);
$collectLabels = static function (array $rows, string $userKey, string $labelKey): array {
$labelsByUser = [];
foreach ($rows as $row) {
$userId = 0;
$label = '';
if (isset($row[$userKey]) && is_array($row[$userKey])) {
$userId = (int) ($row[$userKey]['user_id'] ?? 0);
}
if (isset($row[$labelKey]) && is_array($row[$labelKey])) {
$label = (string) ($row[$labelKey]['description'] ?? '');
}
if ($userId === 0 || $label === '') {
foreach ($row as $value) {
if (!is_array($value)) {
continue;
}
if ($userId === 0 && isset($value['user_id'])) {
$userId = (int) $value['user_id'];
}
if ($label === '' && isset($value['description'])) {
$label = (string) $value['description'];
}
}
}
if ($userId === 0 && isset($row['user_id'])) {
$userId = (int) $row['user_id'];
}
if ($label === '' && isset($row['description'])) {
$label = (string) $row['description'];
}
if ($userId > 0 && $label !== '') {
$labelsByUser[$userId] ??= [];
$labelsByUser[$userId][] = $label;
}
}
return $labelsByUser;
};
foreach ($labelRows as $row) {
$userId = 0;
$tenantId = 0;
$label = '';
if (isset($row['ut']) && is_array($row['ut'])) {
$userId = (int) ($row['ut']['user_id'] ?? 0);
}
if (isset($row['t']) && is_array($row['t'])) {
$tenantId = (int) ($row['t']['tenant_id'] ?? 0);
$label = (string) ($row['t']['description'] ?? '');
}
if ($userId === 0 || $tenantId === 0 || $label === '') {
foreach ($row as $value) {
if (!is_array($value)) {
continue;
}
if ($userId === 0 && isset($value['user_id'])) {
$userId = (int) $value['user_id'];
}
if ($tenantId === 0 && isset($value['tenant_id'])) {
$tenantId = (int) $value['tenant_id'];
}
if ($label === '' && isset($value['description'])) {
$label = (string) $value['description'];
}
}
}
if ($userId === 0 && isset($row['user_id'])) {
$userId = (int) $row['user_id'];
}
if ($tenantId === 0 && isset($row['tenant_id'])) {
$tenantId = (int) $row['tenant_id'];
}
if ($label === '' && isset($row['description'])) {
$label = (string) $row['description'];
}
if ($userId > 0 && $tenantId > 0 && $label !== '') {
$tenantMapByUser[$userId] ??= [];
$tenantMapByUser[$userId][$tenantId] = $label;
}
}
$tenantLabelsByUser = [];
foreach ($tenantMapByUser as $userId => $map) {
$tenantLabelsByUser[$userId] = array_values($map);
}
$roleLabelsByUser = $collectLabels($roleLabelRows, 'ur', 'r');
$departmentLabelsByUser = $collectLabels($departmentLabelRows, 'ud', 'd');
foreach ($list as &$user) {
$userId = (int) ($user['id'] ?? 0);
$primaryId = (int) ($user['primary_tenant_id'] ?? 0);
$user['tenant_labels'] = $tenantLabelsByUser[$userId] ?? [];
if ($primaryId > 0 && isset($tenantMapByUser[$userId][$primaryId])) {
$user['primary_tenant_label'] = $tenantMapByUser[$userId][$primaryId];
}
$user['role_labels'] = $roleLabelsByUser[$userId] ?? [];
$user['department_labels'] = $departmentLabelsByUser[$userId] ?? [];
}
unset($user);
}
$result = [
'total' => $total,
'rows' => $list,
];
return $result;
}
public static function find(int $id): ?array
{
$row = DB::selectOne(
'select id, uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, password, locale, totp_secret, theme, primary_tenant_id, created_by, modified_by, created, modified, active from users where id = ? limit 1',
(string) $id
);
return self::unwrap($row);
}
public static function findByUuid(string $uuid): ?array
{
$row = DB::selectOne(
'select id, uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, password, locale, totp_secret, theme, primary_tenant_id, created_by, modified_by, created, modified, active, active_changed_at, active_changed_by from users where uuid = ? limit 1',
$uuid
);
return self::unwrap($row);
}
public static function findByEmail(string $email): ?array
{
$row = DB::selectOne(
'select id, uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, email_verified_at, email_verified_at, password, locale, totp_secret, theme, primary_tenant_id, created_by, modified_by, created, modified, active, active_changed_at, active_changed_by from users where email = ? limit 1',
$email
);
return self::unwrap($row);
}
private static function uuidV4(): string
{
$data = random_bytes(16);
$data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
$data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
public static function create(array $data)
{
$hash = password_hash($data['password'], PASSWORD_DEFAULT);
return DB::insert(
'insert into users (uuid, first_name, last_name, email, profile_description, job_title, phone, mobile, short_dial, address, postal_code, city, country, region, hire_date, password, locale, totp_secret, theme, primary_tenant_id, current_tenant_id, created_by, created, active, active_changed_at, active_changed_by) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(),?,?,?)',
$data['uuid'] ?? self::uuidV4(),
$data['first_name'],
$data['last_name'],
$data['email'],
$data['profile_description'] ?? null,
$data['job_title'] ?? null,
$data['phone'] ?? null,
$data['mobile'] ?? null,
$data['short_dial'] ?? null,
$data['address'] ?? null,
$data['postal_code'] ?? null,
$data['city'] ?? null,
$data['country'] ?? null,
$data['region'] ?? null,
$data['hire_date'] ?? null,
$hash,
$data['locale'] ?? null,
$data['totp_secret'],
$data['theme'] ?? 'light',
$data['primary_tenant_id'] ?? null,
$data['current_tenant_id'] ?? $data['primary_tenant_id'] ?? null,
$data['created_by'] ?? null,
$data['active'],
$data['active_changed_at'] ?? null,
$data['active_changed_by'] ?? null
);
}
public static function update(int $id, array $data): bool
{
$fields = [
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'email' => $data['email'],
'profile_description' => $data['profile_description'] ?? null,
'job_title' => $data['job_title'] ?? null,
'phone' => $data['phone'] ?? null,
'mobile' => $data['mobile'] ?? null,
'short_dial' => $data['short_dial'] ?? null,
'address' => $data['address'] ?? null,
'postal_code' => $data['postal_code'] ?? null,
'city' => $data['city'] ?? null,
'country' => $data['country'] ?? null,
'region' => $data['region'] ?? null,
'hire_date' => $data['hire_date'] ?? null,
'totp_secret' => $data['totp_secret'],
'active' => $data['active'],
];
if (array_key_exists('locale', $data)) {
$fields['locale'] = $data['locale'];
}
if (array_key_exists('theme', $data)) {
$fields['theme'] = $data['theme'];
}
if (array_key_exists('primary_tenant_id', $data)) {
$fields['primary_tenant_id'] = $data['primary_tenant_id'];
}
if (array_key_exists('current_tenant_id', $data)) {
$fields['current_tenant_id'] = $data['current_tenant_id'];
}
if (array_key_exists('modified_by', $data)) {
$fields['modified_by'] = $data['modified_by'];
}
if (array_key_exists('active_changed_at', $data)) {
$fields['active_changed_at'] = $data['active_changed_at'];
}
if (array_key_exists('active_changed_by', $data)) {
$fields['active_changed_by'] = $data['active_changed_by'];
}
if (!empty($data['password'])) {
$fields['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
}
$setParts = [];
$params = [];
foreach ($fields as $field => $value) {
$setParts[] = sprintf('`%s` = ?', $field);
$params[] = $value;
}
$params[] = (string) $id;
$query = 'update users set ' . implode(', ', $setParts) . ' where id = ?';
$result = DB::update($query, ...$params);
return $result !== false;
}
public static function setActive(int $id, bool $active, ?int $changedBy = null): bool
{
$result = DB::update(
'update users set active = ?, active_changed_at = NOW(), active_changed_by = ? where id = ?',
$active ? 1 : 0,
$changedBy,
(string) $id
);
return $result !== false;
}
public static function setCurrentTenant(int $id, int $tenantId): bool
{
$result = DB::update(
'update users set current_tenant_id = ? where id = ?',
(string) $tenantId,
(string) $id
);
return $result !== false;
}
public static function setActiveByUuids(array $uuids, bool $active, ?int $modifiedBy = null): bool
{
$uuids = array_values(array_filter(array_map('trim', $uuids)));
if (!$uuids) {
return false;
}
$placeholders = implode(',', array_fill(0, count($uuids), '?'));
$query = "update users set active = ?, modified_by = ?, active_changed_at = NOW(), active_changed_by = ? where uuid in ($placeholders)";
$params = array_merge([$active ? 1 : 0, $modifiedBy, $modifiedBy], $uuids);
$result = DB::update($query, ...$params);
return $result !== false;
}
public static function setLocale(int $id, string $locale): bool
{
$result = DB::update('update users set locale = ? where id = ?', $locale, (string) $id);
return $result !== false;
}
public static function setTheme(int $id, string $theme): bool
{
$result = DB::update('update users set theme = ? where id = ?', $theme, (string) $id);
return $result !== false;
}
public static function setPassword(int $id, string $password): bool
{
$hash = password_hash($password, PASSWORD_DEFAULT);
$result = DB::update('update users set password = ?, modified_by = NULL where id = ?', $hash, (string) $id);
return $result !== false;
}
public static function setEmailVerified(int $id): bool
{
$result = DB::update('update users set email_verified_at = NOW() where id = ?', (string) $id);
return $result !== false;
}
public static function delete(int $id): bool
{
$result = DB::delete('delete from users where id = ?', (string) $id);
return $result !== false;
}
public static function deleteByUuids(array $uuids): bool
{
$uuids = array_values(array_filter(array_map('trim', $uuids)));
if (!$uuids) {
return false;
}
$placeholders = implode(',', array_fill(0, count($uuids), '?'));
$query = "delete from users where uuid in ($placeholders)";
$result = DB::delete($query, ...$uuids);
return $result !== false;
}
private static function normalizeIdList($value): array
{
if (is_string($value)) {
$value = array_filter(array_map('trim', explode(',', $value)));
} elseif (!is_array($value)) {
return [];
}
$ids = [];
foreach ($value as $item) {
if ($item === '' || $item === null) {
continue;
}
$ids[] = (int) $item;
}
$ids = array_values(array_filter(array_unique($ids), static function ($id) {
return $id > 0;
}));
return $ids;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class UserRoleRepository
{
public static function listRoleIdsByUserId(int $userId): array
{
$rows = DB::select('select role_id from user_roles where user_id = ?', (string) $userId);
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['user_roles'] ?? $row;
if (is_array($data) && isset($data['role_id'])) {
$ids[] = (int) $data['role_id'];
}
}
return array_values(array_unique($ids));
}
public static function replaceForUser(int $userId, array $roleIds): bool
{
DB::delete('delete from user_roles where user_id = ?', (string) $userId);
if (!$roleIds) {
return true;
}
foreach ($roleIds as $roleId) {
DB::insert(
'insert into user_roles (user_id, role_id, created) values (?,?,NOW())',
(string) $userId,
(string) $roleId
);
}
return true;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace MintyPHP\Repository;
use MintyPHP\DB;
class UserTenantRepository
{
public static function listTenantIdsByUserId(int $userId): array
{
$rows = DB::select('select tenant_id from user_tenants where user_id = ?', (string) $userId);
if (!is_array($rows)) {
return [];
}
$ids = [];
foreach ($rows as $row) {
$data = $row['user_tenants'] ?? $row;
if (is_array($data) && isset($data['tenant_id'])) {
$ids[] = (int) $data['tenant_id'];
}
}
return array_values(array_unique($ids));
}
public static function replaceForUser(int $userId, array $tenantIds): bool
{
DB::delete('delete from user_tenants where user_id = ?', (string) $userId);
if (!$tenantIds) {
return true;
}
foreach ($tenantIds as $tenantId) {
DB::insert(
'insert into user_tenants (user_id, tenant_id, created) values (?,?,NOW())',
(string) $userId,
(string) $tenantId
);
}
return true;
}
}