158 lines
4.4 KiB
PHP
158 lines
4.4 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
declare(strict_types=1);
|
|||
|
|
|
|||
|
|
namespace Modules\RestaurantDelivery\Traits;
|
|||
|
|
|
|||
|
|
use Illuminate\Auth\Access\AuthorizationException;
|
|||
|
|
use Illuminate\Database\Eloquent\Builder;
|
|||
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||
|
|
use Modules\Authentication\Models\Restaurant;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* HasRestaurant Trait
|
|||
|
|
*
|
|||
|
|
* Automatically scopes queries by restaurant_id for multi-restaurant setup.
|
|||
|
|
* Ensures that all records are linked to a restaurant and prevents cross-restaurant access.
|
|||
|
|
*/
|
|||
|
|
trait HasRestaurant
|
|||
|
|
{
|
|||
|
|
/**
|
|||
|
|
* Boot the trait and add global scope
|
|||
|
|
*/
|
|||
|
|
public static function bootHasRestaurant(): void
|
|||
|
|
{
|
|||
|
|
// Global scope: filter by current restaurant automatically
|
|||
|
|
static::addGlobalScope('restaurant', function (Builder $builder) {
|
|||
|
|
if (! config('restaurant-delivery.multi_restaurant.enabled', true)) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$restaurantId = static::getCurrentRestaurantId();
|
|||
|
|
if ($restaurantId) {
|
|||
|
|
$builder->where($builder->getModel()->getTable().'.restaurant_id', $restaurantId);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Auto-set restaurant_id on creating
|
|||
|
|
static::creating(function ($model) {
|
|||
|
|
if (! config('restaurant-delivery.multi_restaurant.enabled', true)) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (empty($model->restaurant_id)) {
|
|||
|
|
$model->restaurant_id = static::getCurrentRestaurantId();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Get current restaurant ID from helper, auth, session, or header
|
|||
|
|
*/
|
|||
|
|
public static function getCurrentRestaurantId(): ?int
|
|||
|
|
{
|
|||
|
|
// 1️⃣ Helper function
|
|||
|
|
if (function_exists('getUserRestaurantId')) {
|
|||
|
|
$restaurantId = getUserRestaurantId();
|
|||
|
|
if ($restaurantId) {
|
|||
|
|
return (int) $restaurantId;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2️⃣ Authenticated user
|
|||
|
|
if (auth()->check()) {
|
|||
|
|
$user = auth()->user();
|
|||
|
|
|
|||
|
|
if (! empty($user->restaurant_id)) {
|
|||
|
|
return (int) $user->restaurant_id;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (method_exists($user, 'getRestaurantId')) {
|
|||
|
|
return (int) $user->getRestaurantId();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (method_exists($user, 'restaurant') && $user->restaurant) {
|
|||
|
|
return (int) $user->restaurant->id;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3️⃣ Session
|
|||
|
|
if (session()->has('restaurant_id')) {
|
|||
|
|
return (int) session('restaurant_id');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 4️⃣ Request header (API)
|
|||
|
|
if (request()->hasHeader('X-Restaurant-ID')) {
|
|||
|
|
return (int) request()->header('X-Restaurant-ID');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Scope to remove restaurant filter (admin or global queries)
|
|||
|
|
*/
|
|||
|
|
public function scopeWithoutRestaurant(Builder $query): Builder
|
|||
|
|
{
|
|||
|
|
return $query->withoutGlobalScope('restaurant');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Scope to filter for a specific restaurant
|
|||
|
|
*/
|
|||
|
|
public function scopeForRestaurant(Builder $query, int $restaurantId): Builder
|
|||
|
|
{
|
|||
|
|
return $query->withoutGlobalScope('restaurant')
|
|||
|
|
->where($this->getTable().'.restaurant_id', $restaurantId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Relationship to the Restaurant model
|
|||
|
|
*/
|
|||
|
|
public function restaurant(): BelongsTo
|
|||
|
|
{
|
|||
|
|
$restaurantModel = config(
|
|||
|
|
'restaurant-delivery.multi_restaurant.restaurant_model',
|
|||
|
|
Restaurant::class
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return $this->belongsTo($restaurantModel, 'restaurant_id');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Check if this record belongs to the current restaurant
|
|||
|
|
*/
|
|||
|
|
public function belongsToCurrentRestaurant(): bool
|
|||
|
|
{
|
|||
|
|
$currentId = static::getCurrentRestaurantId();
|
|||
|
|
|
|||
|
|
return $currentId && $this->restaurant_id === $currentId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Enforce that this record belongs to the current restaurant
|
|||
|
|
*
|
|||
|
|
* @throws AuthorizationException
|
|||
|
|
*/
|
|||
|
|
public function ensureRestaurantOwnership(): void
|
|||
|
|
{
|
|||
|
|
if (! $this->belongsToCurrentRestaurant()) {
|
|||
|
|
throw new AuthorizationException('Access denied: record belongs to another restaurant.');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Helper to optionally auto-scope queries for a specific restaurant or all
|
|||
|
|
*/
|
|||
|
|
public static function queryForRestaurant(?int $restaurantId = null): Builder
|
|||
|
|
{
|
|||
|
|
$builder = static::query();
|
|||
|
|
|
|||
|
|
if ($restaurantId) {
|
|||
|
|
return $builder->forRestaurant($restaurantId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $builder;
|
|||
|
|
}
|
|||
|
|
}
|