feat: add app and database modules
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
abstract class BaseRepository
|
||||
{
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* BaseRepository constructor.
|
||||
*/
|
||||
public function __construct(Model $model)
|
||||
{
|
||||
$this->model = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all records.
|
||||
*/
|
||||
public function all(array $columns = ['*']): Collection
|
||||
{
|
||||
return $this->model->all($columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a record by ID.
|
||||
*/
|
||||
public function find(int|string $id, array $columns = ['*']): ?Model
|
||||
{
|
||||
return $this->model->find($id, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a record by ID or throw an exception.
|
||||
*/
|
||||
public function findOrFail(int|string $id, array $columns = ['*']): Model
|
||||
{
|
||||
return $this->model->findOrFail($id, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record.
|
||||
*/
|
||||
public function create(array $data): Model
|
||||
{
|
||||
return $this->model->create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing record.
|
||||
*/
|
||||
public function update(int|string $id, array $data): bool
|
||||
{
|
||||
$model = $this->find($id);
|
||||
|
||||
if ($model) {
|
||||
return $model->update($data);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a record by ID.
|
||||
*/
|
||||
public function delete(int|string $id): bool
|
||||
{
|
||||
$model = $this->find($id);
|
||||
|
||||
if ($model) {
|
||||
return $model->delete();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginate records.
|
||||
*/
|
||||
public function paginate(int $perPage = 15, array $columns = ['*']): LengthAwarePaginator
|
||||
{
|
||||
return $this->model->paginate($perPage, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query builder.
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
return $this->model->newQuery();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Models\Notification;
|
||||
|
||||
class NotificationRepository extends BaseRepository
|
||||
{
|
||||
public function __construct(Notification $model)
|
||||
{
|
||||
parent::__construct($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of recipient categories a user is authorized to see.
|
||||
* Hierarchy: Developer > Administrator > User > all
|
||||
*/
|
||||
public function getAuthorizedRecipients($user)
|
||||
{
|
||||
$recipients = ['all'];
|
||||
|
||||
if ($user->hasRole('Developer')) {
|
||||
return ['Developer', 'Administrator', 'User', 'all'];
|
||||
}
|
||||
|
||||
if ($user->hasRole('Administrator')) {
|
||||
return ['Administrator', 'User', 'all'];
|
||||
}
|
||||
|
||||
// Default: only see notifications for 'User' and 'all'
|
||||
return ['User', 'all'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ALL notifications for a user based on their roles,
|
||||
* BUT exclude those they have personally deleted/hidden.
|
||||
*/
|
||||
public function getActiveNotificationsForUser($user, $offset = 0, $limit = 10)
|
||||
{
|
||||
$query = $this->model->query();
|
||||
|
||||
// 1. Filter by roles (Broadcast targeting) hierarchical
|
||||
$authorizedRecipients = $this->getAuthorizedRecipients($user);
|
||||
$query->whereIn('recipient', $authorizedRecipients);
|
||||
|
||||
// 2. Left Join personal interactions
|
||||
$query->leftJoin('system_notification_user', function ($join) use ($user) {
|
||||
$join->on('system_notifications.id', '=', 'system_notification_user.notification_id')
|
||||
->where('system_notification_user.user_id', '=', $user->id);
|
||||
});
|
||||
|
||||
// 3. Exclude personally deleted ones
|
||||
$query->whereNull('system_notification_user.deleted_at');
|
||||
|
||||
// 4. Select needed columns (avoiding ID collision with Join)
|
||||
return $query->select('system_notifications.*', 'system_notification_user.read_at as personal_read_at')
|
||||
->latest('system_notifications.created_at')
|
||||
->skip($offset)
|
||||
->take($limit)
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get personal unread count.
|
||||
*/
|
||||
public function getUnreadCount($user)
|
||||
{
|
||||
$query = $this->model->query();
|
||||
|
||||
// Target filtering hierarchical
|
||||
$authorizedRecipients = $this->getAuthorizedRecipients($user);
|
||||
$query->whereIn('recipient', $authorizedRecipients);
|
||||
|
||||
// Join to check read/deleted status
|
||||
$query->leftJoin('system_notification_user', function ($join) use ($user) {
|
||||
$join->on('system_notifications.id', '=', 'system_notification_user.notification_id')
|
||||
->where('system_notification_user.user_id', '=', $user->id);
|
||||
});
|
||||
|
||||
// Unread = (Global read_at is null AND Personal read_at is null) AND not deleted
|
||||
$query->whereNull('system_notification_user.read_at')
|
||||
->whereNull('system_notification_user.deleted_at');
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Personal Mark As Read
|
||||
*/
|
||||
public function markAsRead($notificationId, $userId)
|
||||
{
|
||||
\DB::table('system_notification_user')->updateOrInsert(
|
||||
['notification_id' => $notificationId, 'user_id' => $userId],
|
||||
['read_at' => now(), 'updated_at' => now()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Personal Delete (Hide)
|
||||
*/
|
||||
public function personalDelete($notificationId, $userId)
|
||||
{
|
||||
\DB::table('system_notification_user')->updateOrInsert(
|
||||
['notification_id' => $notificationId, 'user_id' => $userId],
|
||||
['deleted_at' => now(), 'updated_at' => now()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark ALL visible notifications as read for this user.
|
||||
*/
|
||||
public function markAllAsReadForUser($user)
|
||||
{
|
||||
$authorizedRecipients = $this->getAuthorizedRecipients($user);
|
||||
|
||||
// 1. Get IDs of notifications that are visible to user but either:
|
||||
// - Have no entry in pivot (meaning unread)
|
||||
// - Have an entry with read_at IS NULL and deleted_at IS NULL
|
||||
$unreadIds = $this->model->query()
|
||||
->whereIn('recipient', $authorizedRecipients)
|
||||
->leftJoin('system_notification_user', function ($join) use ($user) {
|
||||
$join->on('system_notifications.id', '=', 'system_notification_user.notification_id')
|
||||
->where('system_notification_user.user_id', '=', $user->id);
|
||||
})
|
||||
->whereNull('system_notification_user.read_at')
|
||||
->whereNull('system_notification_user.deleted_at')
|
||||
->pluck('system_notifications.id');
|
||||
|
||||
if ($unreadIds->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Perform batch updateOrInsert for each (or just loop if small, but let's stick to markAsRead helper)
|
||||
foreach ($unreadIds as $id) {
|
||||
$this->markAsRead($id, $user->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Models\SystemSetting;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class SystemSettingRepository
|
||||
{
|
||||
public function tableExists(): bool
|
||||
{
|
||||
return Schema::hasTable('system_settings');
|
||||
}
|
||||
|
||||
public function all(): Collection
|
||||
{
|
||||
return SystemSetting::query()->get();
|
||||
}
|
||||
|
||||
public function allPublic(): Collection
|
||||
{
|
||||
return SystemSetting::query()->where('is_public', true)->get();
|
||||
}
|
||||
|
||||
public function findByKey(string $key): ?SystemSetting
|
||||
{
|
||||
return SystemSetting::query()->where('key', $key)->first();
|
||||
}
|
||||
|
||||
public function upsert(array $payload): SystemSetting
|
||||
{
|
||||
/** @var SystemSetting $setting */
|
||||
$setting = SystemSetting::query()->updateOrCreate(
|
||||
['key' => $payload['key']],
|
||||
[
|
||||
'value' => $payload['value'],
|
||||
'type' => $payload['type'],
|
||||
'group' => $payload['group'],
|
||||
'is_public' => $payload['is_public'],
|
||||
'description' => $payload['description'] ?? null,
|
||||
'created_by' => $payload['created_by'] ?? null,
|
||||
'updated_by' => $payload['updated_by'] ?? null,
|
||||
]
|
||||
);
|
||||
|
||||
return $setting;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class UserRepository extends BaseRepository
|
||||
{
|
||||
public function __construct(User $model)
|
||||
{
|
||||
parent::__construct($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users by role.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getByRole(string $role)
|
||||
{
|
||||
return $this->model->role($role)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search users by name or email.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function search(string $query)
|
||||
{
|
||||
return $this->model->where('name', 'like', "%{$query}%")
|
||||
->orWhere('email', 'like', "%{$query}%")
|
||||
->get();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user