A lot of things
- Added Database page. - Added Xenforo API compatibility - Added Hovercard - Added Notifications
This commit is contained in:
@@ -3,21 +3,12 @@
|
||||
namespace App\Auth;
|
||||
|
||||
use App\Services\XenforoService;
|
||||
use App\XenForoDataTypes\XenForoData;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
|
||||
class XenForoUser implements Authenticatable {
|
||||
class XenForoUser extends XenForoData implements Authenticatable {
|
||||
|
||||
public ?array $permissions = null;
|
||||
private XenforoService $services;
|
||||
|
||||
public function __construct(public readonly object $data) {
|
||||
$this->services = app(XenforoService::class);
|
||||
}
|
||||
|
||||
public function __get(string $name): mixed {
|
||||
return $this->data->$name ?? null;
|
||||
}
|
||||
|
||||
public function getAuthIdentifierName(): string
|
||||
{
|
||||
return 'user_id';
|
||||
|
||||
58
app/Http/Controllers/DynamicLoadController.php
Normal file
58
app/Http/Controllers/DynamicLoadController.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\XenForoHelpers;
|
||||
use App\Services\XenforoApiService;
|
||||
use App\Services\XenforoService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class DynamicLoadController extends Controller
|
||||
{
|
||||
public function hovercard( Request $request, int $user_id ){
|
||||
|
||||
$data = Cache::remember("xf_hovercard_{$user_id}", 300, function() use($user_id){
|
||||
|
||||
$service = app(XenforoService::class);
|
||||
$user = $service->getXfUser( $user_id );
|
||||
|
||||
if( !$user ){
|
||||
return response()->json(['error' => 'User not found'], 404);
|
||||
}
|
||||
|
||||
return [
|
||||
'username' => $user->username,
|
||||
'avatar_url' => $user->getAvatarUrl(),
|
||||
'avatar_color' => XenForoHelpers::getAvatarColor( $user ),
|
||||
'avatar_letter' => XenForoHelpers::getAvatarLetter( $user ),
|
||||
'group_name' => $service->getXfUserGroup( $user?->user_group_id ?? 0 )?->title ?? 'Guest',
|
||||
'joined' => \DateTimeImmutable::createFromTimestamp( $user->register_date ?? 0 )->format('Y-m-d'),
|
||||
'last_seen' => \DateTimeImmutable::createFromTimestamp( $user->last_activity ?? 0 )->format('Y-m-d'),
|
||||
'message_count' => $user->message_count,
|
||||
'reaction_score' => $user->reaction_score,
|
||||
'trophy_points' => $user->trophy_points,
|
||||
'entries_count' => $user->rhpz_entry_count,
|
||||
];
|
||||
|
||||
});
|
||||
|
||||
|
||||
return response()->json( ['user' => $data] );
|
||||
}
|
||||
|
||||
public function getNotifications( Request $request ){
|
||||
|
||||
$service = app(XenforoApiService::class);
|
||||
$data = $service->getUserAlerts(\Auth::user()->user_id);
|
||||
|
||||
return response()->json( $data );
|
||||
}
|
||||
|
||||
public function markAllRead( Request $request ){
|
||||
$service = app(XenforoApiService::class);
|
||||
$service->markAllNotificationsRead(\Auth::user()->user_id);
|
||||
|
||||
return response()->json( ['success' => true] );
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,8 @@ class EntryController extends Controller
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
$entries = Entry::published()
|
||||
->with(['game.platform', 'platform'])
|
||||
->latest('published_at')
|
||||
->paginate(30);
|
||||
|
||||
return view('entries.index', compact('entries'));
|
||||
return view('entries.index');
|
||||
}
|
||||
|
||||
public function show(string $section, Entry $entry): View
|
||||
|
||||
242
app/Livewire/Database.php
Normal file
242
app/Livewire/Database.php
Normal file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Author;
|
||||
use App\Models\Entry;
|
||||
use App\Models\Game;
|
||||
use App\Models\Language;
|
||||
use App\Models\Modification;
|
||||
use App\Models\Platform;
|
||||
use App\Models\Status;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
class Database extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
|
||||
/**
|
||||
* entry_title search
|
||||
* @var string
|
||||
*/
|
||||
public string $search = '';
|
||||
|
||||
/**
|
||||
* type filter.
|
||||
* @var array
|
||||
*/
|
||||
public array $types = [];
|
||||
|
||||
/**
|
||||
* Games IDs filter.
|
||||
* @var array
|
||||
*/
|
||||
public array $games = [];
|
||||
|
||||
/**
|
||||
* Current game search
|
||||
* @var string
|
||||
*/
|
||||
public string $gameSearch = '';
|
||||
|
||||
/**
|
||||
* Platform IDs filter.
|
||||
* @var array
|
||||
*/
|
||||
public array $platforms = [];
|
||||
|
||||
/**
|
||||
* Status IDs filter.
|
||||
* @var array
|
||||
*/
|
||||
public array $statuses = [];
|
||||
|
||||
/**
|
||||
* Authors IDs filter.
|
||||
* @var array
|
||||
*/
|
||||
public array $authors = [];
|
||||
|
||||
/**
|
||||
* Authors mode and/or.
|
||||
* @var string
|
||||
*/
|
||||
public string $authorsMode = 'or';
|
||||
|
||||
/**
|
||||
* Current author search.
|
||||
* @var string
|
||||
*/
|
||||
public string $authorSearch = '';
|
||||
|
||||
/**
|
||||
* Languages IDs filter.
|
||||
* @var array
|
||||
*/
|
||||
public array $languages = [];
|
||||
|
||||
/**
|
||||
* Languages mode and/or
|
||||
* @var string
|
||||
*/
|
||||
public string $languagesMode = 'or';
|
||||
|
||||
/**
|
||||
* Modifications IDs filter.
|
||||
* @var array
|
||||
*/
|
||||
public array $modifications = [];
|
||||
|
||||
/**
|
||||
* Modifications mode and/or.
|
||||
* @var string
|
||||
*/
|
||||
public string $modificationsMode = 'or';
|
||||
|
||||
/**
|
||||
* Sort by field.
|
||||
* @var string
|
||||
*/
|
||||
public string $sortBy = 'created_at';
|
||||
|
||||
/**
|
||||
* asc/desc
|
||||
* @var string
|
||||
*/
|
||||
public string $sortDir = 'desc';
|
||||
|
||||
/**
|
||||
* Translation of sort options key.
|
||||
*/
|
||||
public const array SORT_OPTIONS = [
|
||||
'created_at' => 'Date added',
|
||||
'release_date' => 'Release date',
|
||||
'title' => 'Title'
|
||||
];
|
||||
|
||||
/**
|
||||
* Translation of entries key.
|
||||
*/
|
||||
public const array ENTRY_TYPES = [
|
||||
'translations' => 'Translations',
|
||||
'romhacks' => 'Romhacks',
|
||||
'homebrew' => 'Homebrew',
|
||||
'utilities' => 'Utilities',
|
||||
'documents' => 'Documents',
|
||||
'lua-scripts' => 'Lua Scripts',
|
||||
'tutorials' => 'Tutorials',
|
||||
];
|
||||
|
||||
public const int PAGINATION = 30;
|
||||
|
||||
public function updatedSearch(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedTypes(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedGames(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedPlatforms(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedStatuses(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedAuthors(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedAuthorsMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedLanguages(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedLanguagesMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedModifications(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
public function updatedModificationsMode(): void { $this->resetPage(); $this->dispatch('filters-updated'); }
|
||||
|
||||
public function clearFilters(): void
|
||||
{
|
||||
$this->reset([
|
||||
'search', 'types', 'platforms', 'statuses', 'authors', 'authorsMode', 'languages', 'languagesMode', 'modifications', 'modificationsMode'
|
||||
]);
|
||||
$this->resetPage();
|
||||
}
|
||||
|
||||
public function setSort(string $field): void
|
||||
{
|
||||
if( $this->sortBy === $field ) {
|
||||
$this->sortDir = $this->sortDir === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
$this->sortBy = $field;
|
||||
$this->sortDir = 'asc';
|
||||
}
|
||||
$this->resetPage();
|
||||
$this->dispatch('filters-updated');
|
||||
}
|
||||
|
||||
private function buildQuery()
|
||||
{
|
||||
$query = Entry::query()->published()->with([
|
||||
'game.platform', 'status', 'authors', 'languages'
|
||||
]);
|
||||
|
||||
if( $this->search ) {
|
||||
$query->where(function($q) {
|
||||
$q->where('title', 'like', '%'.$this->search.'%');
|
||||
$q->orWhere('complete_title', 'like', '%'.$this->search.'%');
|
||||
});
|
||||
}
|
||||
|
||||
if( $this->types ) {
|
||||
$query->whereIn('type', $this->types);
|
||||
}
|
||||
|
||||
if( $this->platforms ) {
|
||||
$query->where(function($q) {
|
||||
$q->whereIn('platform_id', $this->platforms)
|
||||
->orWhereHas('game', fn($q2) => $q2->whereIn('platform_id', $this->platforms) );
|
||||
});
|
||||
}
|
||||
|
||||
if( $this->games ){
|
||||
$query->whereIn('game_id', $this->games);
|
||||
}
|
||||
|
||||
if( $this->statuses ) {
|
||||
$query->whereIn('status_id', $this->statuses);
|
||||
}
|
||||
|
||||
if( $this->authors ) {
|
||||
if( $this->authorsMode === 'and' ) {
|
||||
foreach ( $this->authors as $authorId ) {
|
||||
$query->whereHas('authors', fn($q) => $q->where('authors.id', $authorId));
|
||||
}
|
||||
} else {
|
||||
$query->whereHas('authors', fn($q) => $q->whereIn('authors.id', $this->authors));
|
||||
}
|
||||
}
|
||||
|
||||
if( $this->languages ) {
|
||||
if( $this->languagesMode === 'and' ) {
|
||||
foreach ( $this->languages as $langId ) {
|
||||
$query->whereHas('languages', fn($q) => $q->where('languages.id', $langId));
|
||||
}
|
||||
} else {
|
||||
$query->whereHas('languages', fn($q) => $q->whereIn('languages.id', $this->languages));
|
||||
}
|
||||
}
|
||||
|
||||
if( $this->modifications ) {
|
||||
if( $this->modificationsMode === 'and' ) {
|
||||
foreach ( $this->modifications as $modificationId ) {
|
||||
$query->whereHas('modifications', fn($q) => $q->where('modifications.id', $modificationId));
|
||||
}
|
||||
} else {
|
||||
$query->whereHas('modifications', fn($q) => $q->whereIn('modifications.id', $this->modifications));
|
||||
}
|
||||
}
|
||||
|
||||
return $query->orderBy($this->sortBy, $this->sortDir);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.database', [
|
||||
'entries' => $this->buildQuery()->paginate(self::PAGINATION),
|
||||
'allGames' => Game::orderBy('name')->get(),
|
||||
'allPlatforms' => Platform::orderBy('name')->get(),
|
||||
'allStatuses' => Status::orderBy('name')->get(),
|
||||
'allAuthors' => Author::orderBy('name')->get(),
|
||||
'allLanguages' => Language::orderBy('name')->get(),
|
||||
'allModifications' => Modification::orderBy('name')->get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
66
app/Services/XenforoApiService.php
Normal file
66
app/Services/XenforoApiService.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class XenforoApiService {
|
||||
|
||||
private string $apiKey;
|
||||
private int $superUserId;
|
||||
private string $apiUrl;
|
||||
|
||||
public function __construct(){
|
||||
$this->apiKey = config('services.xf_api.key');
|
||||
$this->superUserId = config('services.xf_api.user');
|
||||
$this->apiUrl = config('services.xf_api.url');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ConnectionException
|
||||
*/
|
||||
private function get(string $endpoint, ?int $customUserId = null ): mixed
|
||||
{
|
||||
$response = Http::withHeaders([
|
||||
'XF-Api-Key' => $this->apiKey,
|
||||
'XF-Api-User' => $customUserId ?? $this->superUserId,
|
||||
])->get("{$this->apiUrl}/{$endpoint}");
|
||||
|
||||
if( !$response->ok() )
|
||||
return null;
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
private function post(string $endpoint, ?int $customUserId = null, array $data = [] ): mixed
|
||||
{
|
||||
$response = Http::withHeaders([
|
||||
'XF-Api-Key' => $this->apiKey,
|
||||
'XF-Api-User' => $customUserId ?? $this->superUserId,
|
||||
])->post("{$this->apiUrl}/{$endpoint}", $data);
|
||||
|
||||
if( !$response->ok() )
|
||||
return null;
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
public function getUserAlerts(int $userId): mixed
|
||||
{
|
||||
if( app(XenforoService::class)->getXfUser($userId)?->alerts_unviewed > 0 )
|
||||
return $this->get("alerts?page=1&cutoff=7days", $userId);
|
||||
|
||||
return Cache::remember("xf_alerts_{$userId}", 60, function() use($userId) {
|
||||
return $this->get("alerts?page=1&cutoff=7days", $userId);
|
||||
});
|
||||
}
|
||||
|
||||
public function markAllNotificationsRead(int $userId): void
|
||||
{
|
||||
Cache::forget("xf_alerts_{$userId}");
|
||||
$this->post("alerts/marl-all", $userId );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Auth\XenForoUser;
|
||||
use App\XenForoDataTypes\XenForoUserGroup;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class XenforoService {
|
||||
@@ -10,6 +12,45 @@ class XenforoService {
|
||||
private const int TTL_PERMISSIONS = 300;
|
||||
private const int TTL_ROUTES = 86400;
|
||||
|
||||
/**
|
||||
* Get specific XenForo user.
|
||||
*
|
||||
* @param int $xfUserId
|
||||
*
|
||||
* @return XenForoUser|null
|
||||
*/
|
||||
public function getXfUser( int $xfUserId ): ?XenForoUser {
|
||||
|
||||
$xfUser = \DB::connection('xenforo')
|
||||
->table('user')
|
||||
->where('user_id', $xfUserId)
|
||||
->first();
|
||||
|
||||
if(!$xfUser)
|
||||
return null;
|
||||
|
||||
return new XenForoUser($xfUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific XenForo user group.
|
||||
*
|
||||
* @param int $xfUserGroupId
|
||||
*
|
||||
* @return XenForoUserGroup|null
|
||||
*/
|
||||
public function getXfUserGroup( int $xfUserGroupId ): ?XenForoUserGroup {
|
||||
$xfUserGroup = \DB::connection('xenforo')
|
||||
->table('user_group')
|
||||
->where('user_group_id', $xfUserGroupId)
|
||||
->first();
|
||||
|
||||
if(!$xfUserGroup)
|
||||
return null;
|
||||
|
||||
return new XenForoUserGroup($xfUserGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get permissions for a specific user ID.
|
||||
*
|
||||
|
||||
35
app/View/Components/DatabaseFilterWithMode.php
Normal file
35
app/View/Components/DatabaseFilterWithMode.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class DatabaseFilterWithMode extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string $title,
|
||||
public $items,
|
||||
public string $model,
|
||||
public string $modeModel,
|
||||
public string $selectedMode,
|
||||
|
||||
public string $idProperty = 'id',
|
||||
public string $nameProperty = 'name',
|
||||
)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.database-filter-with-mode');
|
||||
}
|
||||
}
|
||||
35
app/View/Components/DatabaseFilterWithModeSearch.php
Normal file
35
app/View/Components/DatabaseFilterWithModeSearch.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class DatabaseFilterWithModeSearch extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string $title,
|
||||
public $items,
|
||||
public string $model,
|
||||
public string $modeModel,
|
||||
public string $selectedMode,
|
||||
|
||||
public string $idProperty = 'id',
|
||||
public string $nameProperty = 'name',
|
||||
)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.database-filter-with-mode-search');
|
||||
}
|
||||
}
|
||||
33
app/View/Components/DatabaseFilterWithoutMode.php
Normal file
33
app/View/Components/DatabaseFilterWithoutMode.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class DatabaseFilterWithoutMode extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string $title,
|
||||
public $items,
|
||||
public string $model,
|
||||
|
||||
public string $idProperty = 'id',
|
||||
public string $nameProperty = 'name',
|
||||
)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.database-filter-without-mode');
|
||||
}
|
||||
}
|
||||
33
app/View/Components/DatabaseFilterWithoutModeSearch.php
Normal file
33
app/View/Components/DatabaseFilterWithoutModeSearch.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class DatabaseFilterWithoutModeSearch extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string $title,
|
||||
public $items,
|
||||
public string $model,
|
||||
|
||||
public string $idProperty = 'id',
|
||||
public string $nameProperty = 'name',
|
||||
)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.database-filter-without-mode-search');
|
||||
}
|
||||
}
|
||||
44
app/View/Components/EntryCard.php
Normal file
44
app/View/Components/EntryCard.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use App\Models\Entry;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class EntryCard extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* Acronym for entry badge.
|
||||
* TODO: Add in common.css other colors.
|
||||
*/
|
||||
public const array ENTRY_TYPES_BADGE = [
|
||||
'translations' => "Trans",
|
||||
'romhacks' => 'Hack',
|
||||
'homebrew' => 'HBrew',
|
||||
'utilities' => 'Util',
|
||||
'documents' => 'Doc',
|
||||
'lua-scripts' => 'Lua',
|
||||
'tutorials' => 'Tuto'
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public Entry $entry,
|
||||
)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.entry-card');
|
||||
}
|
||||
}
|
||||
35
app/View/Components/XfUsernameLink.php
Normal file
35
app/View/Components/XfUsernameLink.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use App\Auth\XenForoUser;
|
||||
use App\Services\XenforoService;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class XfUsernameLink extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public ?XenForoUser $user = null,
|
||||
public ?int $userId = null
|
||||
)
|
||||
{
|
||||
|
||||
if( $this->user === null && $this->userId !== null ){
|
||||
$this->user = app(XenforoService::class)->getXfUser($this->userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.xf-username-link');
|
||||
}
|
||||
}
|
||||
19
app/XenForoDataTypes/XenForoData.php
Normal file
19
app/XenForoDataTypes/XenForoData.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\XenForoDataTypes;
|
||||
|
||||
use App\Services\XenforoService;
|
||||
|
||||
class XenForoData {
|
||||
|
||||
protected XenforoService $services;
|
||||
|
||||
public function __construct(public readonly object $data) {
|
||||
$this->services = app(XenforoService::class);
|
||||
}
|
||||
|
||||
public function __get(string $name): mixed {
|
||||
return $this->data->$name ?? null;
|
||||
}
|
||||
|
||||
}
|
||||
7
app/XenForoDataTypes/XenForoUserGroup.php
Normal file
7
app/XenForoDataTypes/XenForoUserGroup.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\XenForoDataTypes;
|
||||
|
||||
class XenForoUserGroup extends XenForoData {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user