أُنشئ من Tokal/Test
backend: align API/models with current PostgreSQL schema
فشلت بعض الفحوصات
Deploy Backend / deploy (push) Has been cancelled
فشلت بعض الفحوصات
Deploy Backend / deploy (push) Has been cancelled
هذا الالتزام موجود في:
47
Dockerfile
47
Dockerfile
@@ -1,36 +1,39 @@
|
|||||||
FROM php:8.3-fpm
|
FROM php:8.2-cli
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
# Install system dependencies
|
||||||
libpq-dev \
|
RUN apt-get update && apt-get install -y \
|
||||||
libicu-dev \
|
|
||||||
libpng-dev \
|
|
||||||
libzip-dev \
|
|
||||||
libjpeg62-turbo-dev \
|
|
||||||
libfreetype6-dev \
|
|
||||||
unzip \
|
|
||||||
git \
|
git \
|
||||||
curl \
|
curl \
|
||||||
|
libpng-dev \
|
||||||
|
libonig-dev \
|
||||||
|
libxml2-dev \
|
||||||
zip \
|
zip \
|
||||||
|
unzip \
|
||||||
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
|
# Install PHP extensions
|
||||||
&& docker-php-ext-install -j"$(nproc)" pdo_pgsql pgsql intl gd zip
|
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
|
||||||
|
|
||||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
# Install composer
|
||||||
|
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
WORKDIR /var/www
|
WORKDIR /var/www
|
||||||
|
|
||||||
COPY composer.json composer.lock ./
|
# Copy existing application directory contents
|
||||||
RUN composer install --no-dev --prefer-dist --no-interaction --no-scripts --optimize-autoloader
|
COPY . /var/www
|
||||||
|
|
||||||
COPY . .
|
# Install dependencies
|
||||||
|
RUN composer install --no-interaction --no-dev --optimize-autoloader
|
||||||
|
|
||||||
RUN chown -R www-data:www-data storage bootstrap/cache \
|
# Set permissions
|
||||||
&& chmod -R ug+rw storage bootstrap/cache \
|
RUN chown -R www-data:www-data /var/www \
|
||||||
&& chmod +x docker/entrypoint.sh
|
&& chmod -R 755 /var/www/storage \
|
||||||
|
&& chmod -R 755 /var/www/bootstrap/cache
|
||||||
|
|
||||||
ENTRYPOINT ["docker/entrypoint.sh"]
|
# Expose port 8000
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
EXPOSE 9000
|
# Start Laravel development server
|
||||||
|
CMD php artisan serve --host=0.0.0.0 --port=8000
|
||||||
CMD ["php-fpm"]
|
|
||||||
|
|||||||
@@ -12,10 +12,13 @@ use App\Models\ReservationTableAssignment;
|
|||||||
use App\Models\VenueTable;
|
use App\Models\VenueTable;
|
||||||
use App\Models\ReservationReminder;
|
use App\Models\ReservationReminder;
|
||||||
use App\Models\Venue;
|
use App\Models\Venue;
|
||||||
|
use App\Models\UserBlock;
|
||||||
|
use App\Models\UserStrike;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class ReservationController extends Controller
|
class ReservationController extends Controller
|
||||||
{
|
{
|
||||||
@@ -393,7 +396,7 @@ class ReservationController extends Controller
|
|||||||
private function getPricingRule(?Venue $venue): ?PricingRule
|
private function getPricingRule(?Venue $venue): ?PricingRule
|
||||||
{
|
{
|
||||||
if (! $venue) {
|
if (! $venue) {
|
||||||
return PricingRule::where('scope', 'global')->where('is_active', true)->latest('id')->first();
|
return PricingRule::where('scope', 'global_type')->where('is_active', true)->latest('id')->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
$venueRule = PricingRule::where('scope', 'venue')
|
$venueRule = PricingRule::where('scope', 'venue')
|
||||||
@@ -406,7 +409,7 @@ class ReservationController extends Controller
|
|||||||
return $venueRule;
|
return $venueRule;
|
||||||
}
|
}
|
||||||
|
|
||||||
$typeRule = PricingRule::where('scope', 'type')
|
$typeRule = PricingRule::where('scope', 'global_type')
|
||||||
->where('venue_type', $venue->type)
|
->where('venue_type', $venue->type)
|
||||||
->where('is_active', true)
|
->where('is_active', true)
|
||||||
->latest('id')
|
->latest('id')
|
||||||
@@ -416,7 +419,7 @@ class ReservationController extends Controller
|
|||||||
return $typeRule;
|
return $typeRule;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PricingRule::where('scope', 'global')->where('is_active', true)->latest('id')->first();
|
return PricingRule::where('scope', 'global_type')->where('is_active', true)->latest('id')->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findAvailableTable(Reservation $reservation, bool $includePending, bool $lockRows = false): ?VenueTable
|
private function findAvailableTable(Reservation $reservation, bool $includePending, bool $lockRows = false): ?VenueTable
|
||||||
@@ -501,6 +504,7 @@ class ReservationController extends Controller
|
|||||||
|
|
||||||
return ReservationReminder::create([
|
return ReservationReminder::create([
|
||||||
'reservation_id' => $reservation->id,
|
'reservation_id' => $reservation->id,
|
||||||
|
'user_id' => $reservation->customer_id,
|
||||||
'send_at' => $sendAt,
|
'send_at' => $sendAt,
|
||||||
'sent_at' => null,
|
'sent_at' => null,
|
||||||
]);
|
]);
|
||||||
@@ -563,10 +567,11 @@ class ReservationController extends Controller
|
|||||||
{
|
{
|
||||||
Notification::create([
|
Notification::create([
|
||||||
'user_id' => $reservation->customer_id,
|
'user_id' => $reservation->customer_id,
|
||||||
'type' => $type,
|
'type' => 'reservation_status',
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'body' => $body,
|
'body' => $body,
|
||||||
'data_json' => [
|
'data_json' => [
|
||||||
|
'event' => $type,
|
||||||
'reservation_id' => $reservation->id,
|
'reservation_id' => $reservation->id,
|
||||||
'venue_id' => $reservation->venue_id,
|
'venue_id' => $reservation->venue_id,
|
||||||
'status' => $reservation->status,
|
'status' => $reservation->status,
|
||||||
@@ -596,6 +601,9 @@ class ReservationController extends Controller
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('users', 'strike_count')
|
||||||
|
&& Schema::hasColumn('users', 'blocked_until')
|
||||||
|
&& Schema::hasColumn('users', 'blocked_permanent')) {
|
||||||
$customer->strike_count = $customer->strike_count + 1;
|
$customer->strike_count = $customer->strike_count + 1;
|
||||||
|
|
||||||
if ($customer->strike_count >= 9) {
|
if ($customer->strike_count >= 9) {
|
||||||
@@ -607,6 +615,66 @@ class ReservationController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$customer->save();
|
$customer->save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Schema::hasTable('user_strikes') || ! Schema::hasTable('user_blocks')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserStrike::create([
|
||||||
|
'user_id' => $customer->id,
|
||||||
|
'type' => 'no_show',
|
||||||
|
'created_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$strikeCount = UserStrike::query()
|
||||||
|
->where('user_id', $customer->id)
|
||||||
|
->where('type', 'no_show')
|
||||||
|
->count();
|
||||||
|
|
||||||
|
UserBlock::query()
|
||||||
|
->where('user_id', $customer->id)
|
||||||
|
->where('is_active', true)
|
||||||
|
->update(['is_active' => false]);
|
||||||
|
|
||||||
|
if ($strikeCount >= 9) {
|
||||||
|
UserBlock::create([
|
||||||
|
'user_id' => $customer->id,
|
||||||
|
'level' => 'permanent',
|
||||||
|
'reason' => 'Exceeded no-show threshold',
|
||||||
|
'blocked_until' => null,
|
||||||
|
'created_by' => 'system',
|
||||||
|
'is_active' => true,
|
||||||
|
'created_at' => now(),
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($strikeCount >= 6) {
|
||||||
|
UserBlock::create([
|
||||||
|
'user_id' => $customer->id,
|
||||||
|
'level' => 'month',
|
||||||
|
'reason' => 'Exceeded no-show threshold',
|
||||||
|
'blocked_until' => now()->addDays(30),
|
||||||
|
'created_by' => 'system',
|
||||||
|
'is_active' => true,
|
||||||
|
'created_at' => now(),
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($strikeCount >= 3) {
|
||||||
|
UserBlock::create([
|
||||||
|
'user_id' => $customer->id,
|
||||||
|
'level' => 'week',
|
||||||
|
'reason' => 'Exceeded no-show threshold',
|
||||||
|
'blocked_until' => now()->addDays(7),
|
||||||
|
'created_by' => 'system',
|
||||||
|
'is_active' => true,
|
||||||
|
'created_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function canCancel(Reservation $reservation): bool
|
private function canCancel(Reservation $reservation): bool
|
||||||
|
|||||||
@@ -3,13 +3,17 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Amenity;
|
||||||
|
use App\Models\Offer;
|
||||||
use App\Models\Role;
|
use App\Models\Role;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Venue;
|
use App\Models\Venue;
|
||||||
|
use App\Models\VenueImage;
|
||||||
use App\Models\VenueTable;
|
use App\Models\VenueTable;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
class VenueController extends Controller
|
class VenueController extends Controller
|
||||||
@@ -23,19 +27,8 @@ class VenueController extends Controller
|
|||||||
'per_page' => 'nullable|integer|min:1|max:50',
|
'per_page' => 'nullable|integer|min:1|max:50',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = Venue::query()
|
$query = Venue::query()->select($this->venueSelectColumns());
|
||||||
->select([
|
$this->applyVenueRelations($query);
|
||||||
'id',
|
|
||||||
'name',
|
|
||||||
'type',
|
|
||||||
'description',
|
|
||||||
'address_text',
|
|
||||||
'lat',
|
|
||||||
'lng',
|
|
||||||
'amenities',
|
|
||||||
'image_urls',
|
|
||||||
'offers',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$query->where('is_active', true);
|
$query->where('is_active', true);
|
||||||
|
|
||||||
@@ -50,9 +43,12 @@ class VenueController extends Controller
|
|||||||
$perPage = $validated['per_page'] ?? 10;
|
$perPage = $validated['per_page'] ?? 10;
|
||||||
|
|
||||||
$paginator = $query->orderByDesc('id')->paginate($perPage);
|
$paginator = $query->orderByDesc('id')->paginate($perPage);
|
||||||
|
$data = collect($paginator->items())
|
||||||
|
->map(fn (Venue $venue) => $this->serializeVenue($venue))
|
||||||
|
->values();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'data' => $paginator->items(),
|
'data' => $data,
|
||||||
'meta' => [
|
'meta' => [
|
||||||
'current_page' => $paginator->currentPage(),
|
'current_page' => $paginator->currentPage(),
|
||||||
'per_page' => $paginator->perPage(),
|
'per_page' => $paginator->perPage(),
|
||||||
@@ -66,23 +62,14 @@ class VenueController extends Controller
|
|||||||
public function show(int $id)
|
public function show(int $id)
|
||||||
{
|
{
|
||||||
$venue = Venue::query()
|
$venue = Venue::query()
|
||||||
->select([
|
->select($this->venueSelectColumns())
|
||||||
'id',
|
|
||||||
'name',
|
|
||||||
'type',
|
|
||||||
'description',
|
|
||||||
'address_text',
|
|
||||||
'lat',
|
|
||||||
'lng',
|
|
||||||
'amenities',
|
|
||||||
'image_urls',
|
|
||||||
'offers',
|
|
||||||
])
|
|
||||||
->where('is_active', true)
|
->where('is_active', true)
|
||||||
->findOrFail($id);
|
->findOrFail($id);
|
||||||
|
|
||||||
|
$venue->loadMissing($this->venueRelations());
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'data' => $venue,
|
'data' => $this->serializeVenue($venue),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,24 +98,31 @@ class VenueController extends Controller
|
|||||||
'offers.*.is_active' => 'nullable|boolean',
|
'offers.*.is_active' => 'nullable|boolean',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$offers = $this->sanitizeOffers($validated['offers'] ?? []);
|
||||||
|
$imageUrls = $this->appendUploadedImages($request, $validated['image_urls'] ?? []);
|
||||||
$venue = Venue::create([
|
$venue = Venue::create([
|
||||||
$ownerColumn => $user->id,
|
$ownerColumn => $user->id,
|
||||||
'name' => $validated['name'],
|
'name' => $validated['name'],
|
||||||
'type' => $validated['type'],
|
'type' => $validated['type'],
|
||||||
'description' => $validated['description'] ?? null,
|
'description' => $validated['description'] ?? null,
|
||||||
'address_text' => $validated['address_text'] ?? null,
|
'address_text' => $validated['address_text'] ?? null,
|
||||||
'lat' => $validated['lat'] ?? null,
|
'lat' => $validated['lat'] ?? 0,
|
||||||
'lng' => $validated['lng'] ?? null,
|
'lng' => $validated['lng'] ?? 0,
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
'phone' => $validated['phone'] ?? null,
|
'phone' => $validated['phone'] ?? null,
|
||||||
'amenities' => $validated['amenities'] ?? [],
|
...$this->inlineVenuePayload($validated['amenities'] ?? [], $imageUrls, $offers),
|
||||||
'image_urls' => $validated['image_urls'] ?? [],
|
|
||||||
'offers' => $this->sanitizeOffers($validated['offers'] ?? []),
|
|
||||||
]);
|
]);
|
||||||
|
$this->syncStructuredVenueData(
|
||||||
|
$venue,
|
||||||
|
$validated['amenities'] ?? [],
|
||||||
|
$imageUrls,
|
||||||
|
$offers
|
||||||
|
);
|
||||||
|
$venue = $this->reloadVenueForResponse($venue->id);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Venue created successfully',
|
'message' => 'Venue created successfully',
|
||||||
'venue' => $venue,
|
'venue' => $this->serializeVenue($venue),
|
||||||
], 201);
|
], 201);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,25 +137,15 @@ class VenueController extends Controller
|
|||||||
|
|
||||||
$query = Venue::query()
|
$query = Venue::query()
|
||||||
->select([
|
->select([
|
||||||
'id',
|
|
||||||
DB::raw($ownerColumn . ' as vendor_id'),
|
DB::raw($ownerColumn . ' as vendor_id'),
|
||||||
'name',
|
...$this->venueSelectColumns(),
|
||||||
'type',
|
|
||||||
'description',
|
|
||||||
'address_text',
|
|
||||||
'lat',
|
|
||||||
'lng',
|
|
||||||
'is_active',
|
|
||||||
'phone',
|
|
||||||
'amenities',
|
|
||||||
'image_urls',
|
|
||||||
'offers',
|
|
||||||
])
|
])
|
||||||
->withCount([
|
->withCount([
|
||||||
'tables as table_count' => function ($q) {
|
'tables as table_count' => function ($q) {
|
||||||
$q->where('is_active', true);
|
$q->where('is_active', true);
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
$this->applyVenueRelations($query);
|
||||||
|
|
||||||
if (! empty($validated['search'])) {
|
if (! empty($validated['search'])) {
|
||||||
$query->where('name', 'like', '%' . $validated['search'] . '%');
|
$query->where('name', 'like', '%' . $validated['search'] . '%');
|
||||||
@@ -169,9 +153,12 @@ class VenueController extends Controller
|
|||||||
|
|
||||||
$perPage = $validated['per_page'] ?? 20;
|
$perPage = $validated['per_page'] ?? 20;
|
||||||
$paginator = $query->orderByDesc('id')->paginate($perPage);
|
$paginator = $query->orderByDesc('id')->paginate($perPage);
|
||||||
|
$data = collect($paginator->items())
|
||||||
|
->map(fn (Venue $venue) => $this->serializeVenue($venue, true))
|
||||||
|
->values();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'data' => $paginator->items(),
|
'data' => $data,
|
||||||
'meta' => [
|
'meta' => [
|
||||||
'current_page' => $paginator->currentPage(),
|
'current_page' => $paginator->currentPage(),
|
||||||
'per_page' => $paginator->perPage(),
|
'per_page' => $paginator->perPage(),
|
||||||
@@ -225,7 +212,7 @@ class VenueController extends Controller
|
|||||||
'last_name' => 'User',
|
'last_name' => 'User',
|
||||||
'email' => $validated['vendor_email'],
|
'email' => $validated['vendor_email'],
|
||||||
'phone' => $validated['vendor_phone'],
|
'phone' => $validated['vendor_phone'],
|
||||||
'password' => Hash::make($validated['vendor_password']),
|
'password_hash' => Hash::make($validated['vendor_password']),
|
||||||
]);
|
]);
|
||||||
$vendor->roles()->syncWithoutDetaching([$vendorRole->id]);
|
$vendor->roles()->syncWithoutDetaching([$vendorRole->id]);
|
||||||
$vendorId = $vendor->id;
|
$vendorId = $vendor->id;
|
||||||
@@ -245,6 +232,7 @@ class VenueController extends Controller
|
|||||||
$ownerColumn = Venue::ownerColumn();
|
$ownerColumn = Venue::ownerColumn();
|
||||||
$imageUrls = $validated['image_urls'] ?? [];
|
$imageUrls = $validated['image_urls'] ?? [];
|
||||||
$imageUrls = $this->appendUploadedImages($request, $imageUrls);
|
$imageUrls = $this->appendUploadedImages($request, $imageUrls);
|
||||||
|
$offers = $this->sanitizeOffers($validated['offers'] ?? []);
|
||||||
|
|
||||||
$venue = Venue::create([
|
$venue = Venue::create([
|
||||||
$ownerColumn => $vendorId,
|
$ownerColumn => $vendorId,
|
||||||
@@ -252,25 +240,28 @@ class VenueController extends Controller
|
|||||||
'type' => $validated['type'],
|
'type' => $validated['type'],
|
||||||
'description' => $validated['description'] ?? null,
|
'description' => $validated['description'] ?? null,
|
||||||
'address_text' => $validated['address_text'] ?? null,
|
'address_text' => $validated['address_text'] ?? null,
|
||||||
'lat' => $validated['lat'] ?? null,
|
'lat' => $validated['lat'] ?? 0,
|
||||||
'lng' => $validated['lng'] ?? null,
|
'lng' => $validated['lng'] ?? 0,
|
||||||
'is_active' => $validated['is_active'] ?? true,
|
'is_active' => $validated['is_active'] ?? true,
|
||||||
'phone' => $validated['phone'] ?? null,
|
'phone' => $validated['phone'] ?? null,
|
||||||
'amenities' => $validated['amenities'] ?? [],
|
...$this->inlineVenuePayload($validated['amenities'] ?? [], $imageUrls, $offers),
|
||||||
'image_urls' => $imageUrls,
|
|
||||||
'offers' => $this->sanitizeOffers($validated['offers'] ?? []),
|
|
||||||
]);
|
]);
|
||||||
|
$this->syncStructuredVenueData(
|
||||||
|
$venue,
|
||||||
|
$validated['amenities'] ?? [],
|
||||||
|
$imageUrls,
|
||||||
|
$offers
|
||||||
|
);
|
||||||
|
|
||||||
$this->syncVenueTables($venue, (int) ($validated['table_count'] ?? 4));
|
$this->syncVenueTables($venue, (int) ($validated['table_count'] ?? 4));
|
||||||
});
|
});
|
||||||
|
$venue = $this->reloadVenueForResponse($venue->id);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Venue created successfully',
|
'message' => 'Venue created successfully',
|
||||||
'venue' => $venue->loadCount([
|
'venue' => $this->serializeVenue($venue->loadCount([
|
||||||
'tables as table_count' => function ($q) {
|
'tables as table_count' => fn ($q) => $q->where('is_active', true),
|
||||||
$q->where('is_active', true);
|
]), true),
|
||||||
},
|
|
||||||
]),
|
|
||||||
], 201);
|
], 201);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,26 +298,62 @@ class VenueController extends Controller
|
|||||||
$validated['offers'] = $this->sanitizeOffers($validated['offers'] ?? []);
|
$validated['offers'] = $this->sanitizeOffers($validated['offers'] ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
$baseImageUrls = array_key_exists('image_urls', $validated)
|
$hasAmenityInput = array_key_exists('amenities', $validated);
|
||||||
|
$hasOfferInput = array_key_exists('offers', $validated);
|
||||||
|
$hasImageInput = array_key_exists('image_urls', $validated) || $request->hasFile('images');
|
||||||
|
$resolvedImageUrls = $hasImageInput
|
||||||
|
? $this->appendUploadedImages(
|
||||||
|
$request,
|
||||||
|
array_key_exists('image_urls', $validated)
|
||||||
? ($validated['image_urls'] ?? [])
|
? ($validated['image_urls'] ?? [])
|
||||||
: ($venue->image_urls ?? []);
|
: $this->resolveImageUrls($venue)
|
||||||
$validated['image_urls'] = $this->appendUploadedImages($request, $baseImageUrls);
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
$venue->fill($validated);
|
$payload = collect($validated)->only([
|
||||||
|
'vendor_id',
|
||||||
|
'name',
|
||||||
|
'type',
|
||||||
|
'description',
|
||||||
|
'address_text',
|
||||||
|
'lat',
|
||||||
|
'lng',
|
||||||
|
'is_active',
|
||||||
|
'phone',
|
||||||
|
])->all();
|
||||||
|
|
||||||
|
if ($this->hasInlineVenueArrays()) {
|
||||||
|
if ($hasAmenityInput) {
|
||||||
|
$payload['amenities'] = $validated['amenities'] ?? [];
|
||||||
|
}
|
||||||
|
if ($hasOfferInput) {
|
||||||
|
$payload['offers'] = $validated['offers'] ?? [];
|
||||||
|
}
|
||||||
|
if ($hasImageInput) {
|
||||||
|
$payload['image_urls'] = $resolvedImageUrls ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$venue->fill($payload);
|
||||||
$venue->save();
|
$venue->save();
|
||||||
|
$this->syncStructuredVenueData(
|
||||||
|
$venue,
|
||||||
|
$hasAmenityInput ? ($validated['amenities'] ?? []) : null,
|
||||||
|
$hasImageInput ? ($resolvedImageUrls ?? []) : null,
|
||||||
|
$hasOfferInput ? ($validated['offers'] ?? []) : null
|
||||||
|
);
|
||||||
|
|
||||||
if (array_key_exists('table_count', $validated)) {
|
if (array_key_exists('table_count', $validated)) {
|
||||||
$this->syncVenueTables($venue, (int) $validated['table_count']);
|
$this->syncVenueTables($venue, (int) $validated['table_count']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$venue = $this->reloadVenueForResponse($venue->id)->loadCount([
|
||||||
|
'tables as table_count' => fn ($q) => $q->where('is_active', true),
|
||||||
|
]);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Venue updated successfully',
|
'message' => 'Venue updated successfully',
|
||||||
'venue' => $venue->loadCount([
|
'venue' => $this->serializeVenue($venue, true),
|
||||||
'tables as table_count' => function ($q) {
|
|
||||||
$q->where('is_active', true);
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +396,7 @@ class VenueController extends Controller
|
|||||||
VenueTable::create([
|
VenueTable::create([
|
||||||
'venue_id' => $venue->id,
|
'venue_id' => $venue->id,
|
||||||
'seating_area_id' => null,
|
'seating_area_id' => null,
|
||||||
'name' => 'Table ' . $i,
|
VenueTable::labelColumn() => 'Table ' . $i,
|
||||||
'capacity' => 4,
|
'capacity' => 4,
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
]);
|
]);
|
||||||
@@ -429,4 +456,207 @@ class VenueController extends Controller
|
|||||||
|
|
||||||
return $urls;
|
return $urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function venueSelectColumns(): array
|
||||||
|
{
|
||||||
|
$columns = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'type',
|
||||||
|
'description',
|
||||||
|
'address_text',
|
||||||
|
'lat',
|
||||||
|
'lng',
|
||||||
|
'is_active',
|
||||||
|
'phone',
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->hasInlineVenueArrays()) {
|
||||||
|
$columns[] = 'amenities';
|
||||||
|
$columns[] = 'image_urls';
|
||||||
|
$columns[] = 'offers';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function venueRelations(): array
|
||||||
|
{
|
||||||
|
if (! $this->hasStructuredVenueData()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'images:id,venue_id,url,sort_order',
|
||||||
|
'offersRelation:id,venue_id,title,description,image_url,is_active,start_at,end_at',
|
||||||
|
'amenitiesRelation:id,name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyVenueRelations($query): void
|
||||||
|
{
|
||||||
|
$relations = $this->venueRelations();
|
||||||
|
if (! empty($relations)) {
|
||||||
|
$query->with($relations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function reloadVenueForResponse(int $venueId): Venue
|
||||||
|
{
|
||||||
|
$query = Venue::query()->select($this->venueSelectColumns());
|
||||||
|
$this->applyVenueRelations($query);
|
||||||
|
|
||||||
|
return $query->findOrFail($venueId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function serializeVenue(Venue $venue, bool $includeAdminFields = false): array
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'id' => $venue->id,
|
||||||
|
'name' => $venue->name,
|
||||||
|
'type' => $venue->type,
|
||||||
|
'description' => $venue->description,
|
||||||
|
'address_text' => $venue->address_text,
|
||||||
|
'lat' => $venue->lat,
|
||||||
|
'lng' => $venue->lng,
|
||||||
|
'phone' => $venue->phone,
|
||||||
|
'is_active' => (bool) $venue->is_active,
|
||||||
|
'amenities' => $this->resolveAmenities($venue),
|
||||||
|
'image_urls' => $this->resolveImageUrls($venue),
|
||||||
|
'offers' => $this->resolveOffers($venue),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($includeAdminFields) {
|
||||||
|
$ownerColumn = Venue::ownerColumn();
|
||||||
|
$data['vendor_id'] = $venue->vendor_id ?? $venue->{$ownerColumn} ?? null;
|
||||||
|
$data['table_count'] = $venue->table_count ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveAmenities(Venue $venue): array
|
||||||
|
{
|
||||||
|
if ($this->hasInlineVenueArrays()) {
|
||||||
|
return is_array($venue->amenities) ? $venue->amenities : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->hasStructuredVenueData()) {
|
||||||
|
return ($venue->amenitiesRelation ?? collect())
|
||||||
|
->pluck('name')
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveImageUrls(Venue $venue): array
|
||||||
|
{
|
||||||
|
if ($this->hasInlineVenueArrays()) {
|
||||||
|
return is_array($venue->image_urls) ? $venue->image_urls : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->hasStructuredVenueData()) {
|
||||||
|
return ($venue->images ?? collect())
|
||||||
|
->sortBy('sort_order')
|
||||||
|
->pluck('url')
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveOffers(Venue $venue): array
|
||||||
|
{
|
||||||
|
if ($this->hasInlineVenueArrays()) {
|
||||||
|
return is_array($venue->offers) ? $venue->offers : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->hasStructuredVenueData()) {
|
||||||
|
return ($venue->offersRelation ?? collect())
|
||||||
|
->map(fn (Offer $offer) => [
|
||||||
|
'title' => $offer->title,
|
||||||
|
'description' => $offer->description,
|
||||||
|
'image_url' => $offer->image_url,
|
||||||
|
'is_active' => (bool) $offer->is_active,
|
||||||
|
])
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hasInlineVenueArrays(): bool
|
||||||
|
{
|
||||||
|
return Schema::hasColumn('venues', 'amenities')
|
||||||
|
&& Schema::hasColumn('venues', 'image_urls')
|
||||||
|
&& Schema::hasColumn('venues', 'offers');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hasStructuredVenueData(): bool
|
||||||
|
{
|
||||||
|
return Schema::hasTable('venue_images')
|
||||||
|
&& Schema::hasTable('offers')
|
||||||
|
&& Schema::hasTable('venue_amenities')
|
||||||
|
&& Schema::hasTable('amenities');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function inlineVenuePayload(array $amenities, array $imageUrls, array $offers): array
|
||||||
|
{
|
||||||
|
if (! $this->hasInlineVenueArrays()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'amenities' => $amenities,
|
||||||
|
'image_urls' => $imageUrls,
|
||||||
|
'offers' => $offers,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncStructuredVenueData(Venue $venue, ?array $amenities, ?array $imageUrls, ?array $offers): void
|
||||||
|
{
|
||||||
|
if (! $this->hasStructuredVenueData()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($amenities !== null) {
|
||||||
|
$amenityIds = collect($amenities)
|
||||||
|
->map(fn ($name) => trim((string) $name))
|
||||||
|
->filter(fn ($name) => $name !== '')
|
||||||
|
->map(fn ($name) => Amenity::firstOrCreate(['name' => $name])->id)
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
$venue->amenitiesRelation()->sync($amenityIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($imageUrls !== null) {
|
||||||
|
VenueImage::query()->where('venue_id', $venue->id)->delete();
|
||||||
|
foreach (array_values($imageUrls) as $index => $url) {
|
||||||
|
VenueImage::create([
|
||||||
|
'venue_id' => $venue->id,
|
||||||
|
'url' => $url,
|
||||||
|
'sort_order' => $index,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($offers !== null) {
|
||||||
|
Offer::query()->where('venue_id', $venue->id)->delete();
|
||||||
|
foreach ($offers as $offer) {
|
||||||
|
Offer::create([
|
||||||
|
'venue_id' => $venue->id,
|
||||||
|
'title' => $offer['title'],
|
||||||
|
'description' => $offer['description'] ?: null,
|
||||||
|
'image_url' => $offer['image_url'] ?: null,
|
||||||
|
'is_active' => $offer['is_active'] ?? true,
|
||||||
|
'start_at' => now(),
|
||||||
|
'end_at' => now()->addDays(30),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
app/Models/Amenity.php
Normal file
17
app/Models/Amenity.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
class Amenity extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
30
app/Models/Offer.php
Normal file
30
app/Models/Offer.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
class Offer extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'venue_id',
|
||||||
|
'title',
|
||||||
|
'description',
|
||||||
|
'image_url',
|
||||||
|
'start_at',
|
||||||
|
'end_at',
|
||||||
|
'is_active',
|
||||||
|
'created_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'is_active' => 'boolean',
|
||||||
|
'start_at' => 'datetime',
|
||||||
|
'end_at' => 'datetime',
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -4,21 +4,43 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class ReservationStatusHistory extends Model
|
class ReservationStatusHistory extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
protected static ?string $resolvedTable = null;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'reservation_id',
|
'reservation_id',
|
||||||
'old_status',
|
'old_status',
|
||||||
'new_status',
|
'new_status',
|
||||||
'changed_by_user_id',
|
'changed_by_user_id',
|
||||||
|
'note',
|
||||||
'created_at',
|
'created_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
parent::__construct($attributes);
|
||||||
|
$this->table = self::tableName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tableName(): string
|
||||||
|
{
|
||||||
|
if (self::$resolvedTable !== null) {
|
||||||
|
return self::$resolvedTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$resolvedTable = Schema::hasTable('reservation_status_histories')
|
||||||
|
? 'reservation_status_histories'
|
||||||
|
: 'reservation_status_history';
|
||||||
|
|
||||||
|
return self::$resolvedTable;
|
||||||
|
}
|
||||||
|
|
||||||
public function reservation()
|
public function reservation()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Reservation::class);
|
return $this->belongsTo(Reservation::class);
|
||||||
|
|||||||
@@ -4,15 +4,19 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class ReservationTableAssignment extends Model
|
class ReservationTableAssignment extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
protected static ?string $resolvedTable = null;
|
||||||
|
protected static ?string $resolvedTableForeignKey = null;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'reservation_id',
|
'reservation_id',
|
||||||
|
'table_id',
|
||||||
'venue_table_id',
|
'venue_table_id',
|
||||||
'assigned_at',
|
'assigned_at',
|
||||||
];
|
];
|
||||||
@@ -21,6 +25,49 @@ class ReservationTableAssignment extends Model
|
|||||||
'assigned_at' => 'datetime',
|
'assigned_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
parent::__construct($attributes);
|
||||||
|
$this->table = self::tableName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tableName(): string
|
||||||
|
{
|
||||||
|
if (self::$resolvedTable !== null) {
|
||||||
|
return self::$resolvedTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$resolvedTable = Schema::hasTable('reservation_table_assignments')
|
||||||
|
? 'reservation_table_assignments'
|
||||||
|
: 'reservation_tables';
|
||||||
|
|
||||||
|
return self::$resolvedTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tableForeignKey(): string
|
||||||
|
{
|
||||||
|
if (self::$resolvedTableForeignKey !== null) {
|
||||||
|
return self::$resolvedTableForeignKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$resolvedTableForeignKey = Schema::hasColumn(self::tableName(), 'venue_table_id')
|
||||||
|
? 'venue_table_id'
|
||||||
|
: 'table_id';
|
||||||
|
|
||||||
|
return self::$resolvedTableForeignKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setVenueTableIdAttribute($value): void
|
||||||
|
{
|
||||||
|
$this->attributes[self::tableForeignKey()] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVenueTableIdAttribute()
|
||||||
|
{
|
||||||
|
$column = self::tableForeignKey();
|
||||||
|
return $this->attributes[$column] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
public function reservation()
|
public function reservation()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Reservation::class);
|
return $this->belongsTo(Reservation::class);
|
||||||
@@ -28,6 +75,6 @@ class ReservationTableAssignment extends Model
|
|||||||
|
|
||||||
public function table()
|
public function table()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(VenueTable::class, 'venue_table_id');
|
return $this->belongsTo(VenueTable::class, self::tableForeignKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,15 @@ use App\Models\Reservation;
|
|||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use App\Models\Notification;
|
use App\Models\Notification;
|
||||||
use App\Models\UserFeedback;
|
use App\Models\UserFeedback;
|
||||||
|
use App\Models\UserBlock;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
use HasApiTokens, HasFactory, Notifiable;
|
use HasApiTokens, HasFactory, Notifiable;
|
||||||
|
protected static ?bool $hasLegacyBlockColumns = null;
|
||||||
|
protected static ?bool $hasUserBlocksTable = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
@@ -86,6 +90,7 @@ class User extends Authenticatable
|
|||||||
|
|
||||||
public function isBlocked(): bool
|
public function isBlocked(): bool
|
||||||
{
|
{
|
||||||
|
if ($this->hasLegacyBlockColumns()) {
|
||||||
if ($this->blocked_permanent) {
|
if ($this->blocked_permanent) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -96,4 +101,42 @@ class User extends Authenticatable
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! $this->hasUserBlocksTable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserBlock::query()
|
||||||
|
->where('user_id', $this->id)
|
||||||
|
->where('is_active', true)
|
||||||
|
->where(function ($q) {
|
||||||
|
$q->whereNull('blocked_until')
|
||||||
|
->orWhere('blocked_until', '>', now());
|
||||||
|
})
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hasLegacyBlockColumns(): bool
|
||||||
|
{
|
||||||
|
if (self::$hasLegacyBlockColumns !== null) {
|
||||||
|
return self::$hasLegacyBlockColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$hasLegacyBlockColumns = Schema::hasColumn('users', 'blocked_until')
|
||||||
|
&& Schema::hasColumn('users', 'blocked_permanent')
|
||||||
|
&& Schema::hasColumn('users', 'strike_count');
|
||||||
|
|
||||||
|
return self::$hasLegacyBlockColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hasUserBlocksTable(): bool
|
||||||
|
{
|
||||||
|
if (self::$hasUserBlocksTable !== null) {
|
||||||
|
return self::$hasUserBlocksTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$hasUserBlocksTable = Schema::hasTable('user_blocks');
|
||||||
|
|
||||||
|
return self::$hasUserBlocksTable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
app/Models/UserBlock.php
Normal file
32
app/Models/UserBlock.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
class UserBlock extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $table = 'user_blocks';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'user_id',
|
||||||
|
'level',
|
||||||
|
'reason',
|
||||||
|
'blocked_until',
|
||||||
|
'created_by',
|
||||||
|
'created_by_user_id',
|
||||||
|
'trigger_venue_id',
|
||||||
|
'is_active',
|
||||||
|
'created_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'blocked_until' => 'datetime',
|
||||||
|
'is_active' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
23
app/Models/UserStrike.php
Normal file
23
app/Models/UserStrike.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
class UserStrike extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $table = 'user_strikes';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'user_id',
|
||||||
|
'reservation_id',
|
||||||
|
'venue_id',
|
||||||
|
'type',
|
||||||
|
'created_at',
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ use Illuminate\Support\Facades\Schema;
|
|||||||
use App\Models\Reservation;
|
use App\Models\Reservation;
|
||||||
use App\Models\SeatingArea;
|
use App\Models\SeatingArea;
|
||||||
use App\Models\VenueTable;
|
use App\Models\VenueTable;
|
||||||
|
use App\Models\Offer;
|
||||||
|
use App\Models\VenueImage;
|
||||||
|
use App\Models\Amenity;
|
||||||
|
|
||||||
class Venue extends Model
|
class Venue extends Model
|
||||||
{
|
{
|
||||||
@@ -29,6 +32,7 @@ class Venue extends Model
|
|||||||
'amenities',
|
'amenities',
|
||||||
'image_urls',
|
'image_urls',
|
||||||
'offers',
|
'offers',
|
||||||
|
'created_by_admin_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
@@ -80,4 +84,19 @@ class Venue extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(VenueTable::class);
|
return $this->hasMany(VenueTable::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function images()
|
||||||
|
{
|
||||||
|
return $this->hasMany(VenueImage::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offersRelation()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Offer::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function amenitiesRelation()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Amenity::class, 'venue_amenities', 'venue_id', 'amenity_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
app/Models/VenueImage.php
Normal file
21
app/Models/VenueImage.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
class VenueImage extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $table = 'venue_images';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'venue_id',
|
||||||
|
'url',
|
||||||
|
'sort_order',
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -7,6 +7,10 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
|
"ext-gd": "*",
|
||||||
|
"ext-intl": "*",
|
||||||
|
"ext-pdo_pgsql": "*",
|
||||||
|
"ext-zip": "*",
|
||||||
"laravel/framework": "^12.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/sanctum": "^4.3",
|
"laravel/sanctum": "^4.3",
|
||||||
"laravel/tinker": "^2.10.1"
|
"laravel/tinker": "^2.10.1"
|
||||||
|
|||||||
8
composer.lock
مولّد
8
composer.lock
مولّد
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "ad91ab9bde70e3576f2b64dd11542bc4",
|
"content-hash": "bf1df9d7ebfcbdc683785439b472c678",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -8452,7 +8452,11 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^8.2"
|
"php": "^8.2",
|
||||||
|
"ext-gd": "*",
|
||||||
|
"ext-intl": "*",
|
||||||
|
"ext-pdo_pgsql": "*",
|
||||||
|
"ext-zip": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": {},
|
"platform-dev": {},
|
||||||
"plugin-api-version": "2.9.0"
|
"plugin-api-version": "2.9.0"
|
||||||
|
|||||||
المرجع في مشكلة جديدة
حظر مستخدم