Skip to content
Merged

f #9

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Saylors-Mirissa (Laravel)

This repository is a Laravel application. Below are concise setup, migration, and seeding instructions so you can get the database and dev environment running locally.

## Prerequisites

- PHP (8.x recommended)
- Composer
- Node.js + npm (for building assets)
- A database (MySQL, Postgres, or SQLite)

## Quick setup

1. Install PHP dependencies

```bash
composer install
```

2. Install frontend deps and build (optional for dev)

```bash
npm install
npm run dev
```

3. Environment

Copy or create your `.env` file and configure DB settings. If the repo contains an `.env.example` file:

```bash
cp .env.example .env
```

Ensure database credentials in `.env` are correct. For SQLite you can create a file:

```bash
touch database/database.sqlite
# then set DB_CONNECTION=sqlite in .env
```

4. Generate the app key

```bash
php artisan key:generate
```

## Migrate and seed

Run all migrations and seeders in one command:

```bash
php artisan migrate --seed
```

Alternative: migrate and then seed explicitly

```bash
php artisan migrate
php artisan db:seed
```

Run a single seeder class (examples in this repo):

```bash
php artisan db:seed --class=ExperienceSeeder
php artisan db:seed --class=MapPointSeeder
php artisan db:seed --class=RoomSeeder
```

Reset and re-seed the database (dangerous: drops all tables):

```bash
php artisan migrate:fresh --seed
```

Rollback the last migration batch:

```bash
php artisan migrate:rollback
```

## Useful commands

- Create storage symlink for public access to storage files:

```bash
php artisan storage:link
```

- Run the built-in dev server:

```bash
php artisan serve
```

- Run tests (Pest or PHPUnit):

```bash
./vendor/bin/pest
# or
php artisan test
```

## Seeders in this repository

Look in `database/seeders/` for available seeders. At time of writing this repo includes:

- `DatabaseSeeder.php`
- `ExperienceSeeder.php`
- `MapPointSeeder.php`
- `RoomSeeder.php`

Run specific seeders with `php artisan db:seed --class=YourSeederClass` as shown above.

## Troubleshooting

- If migrations fail with SQL errors, verify `.env` DB credentials and that the database exists.
- For permission errors when writing to `storage` or `bootstrap/cache`, run:

```bash
sudo chown -R $USER:www-data storage bootstrap/cache
chmod -R ug+rwx storage bootstrap/cache
```

- If you change `.env`, restart services or re-run `php artisan config:clear` and `php artisan cache:clear`:

```bash
php artisan config:clear
php artisan cache:clear
```

## Next steps

- After migrations and seeding, visit the app via `php artisan serve` or your configured web server.
- If you'd like, I can: run migrations on this machine, create a script to automate setup, or add a Docker setup — tell me which.

---

66 changes: 41 additions & 25 deletions app/Http/Controllers/Admin/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@ public function store(Request $request)
$validated = $request->validate([
'floor_id' => 'required|string',
'floor_view' => 'required|string|max:255',
// 'floor_coords' => 'required|string', // Removed
// 'floor_name' => 'required|string', // Auto-generated
'room_number' => 'required|integer',
'room_name' => 'required|string|max:255',
'room_type' => 'required|string|in:double,twin',
'price' => 'required|string|max:255',
'description' => 'required|string',
'image' => 'required|image|max:5120',
'images.*' => 'required|image|max:5120',
'order' => 'required|integer|min:0',
]);

// Handle image upload
$image = $request->file('image');
$imageName = time() . '_room_' . $validated['room_number'] . '.' . $image->getClientOriginalExtension();
$image->move(public_path('images/rooms'), $imageName);
// Handle multiple image uploads
$imagePaths = [];
if ($request->hasFile('images')) {
foreach ($request->file('images') as $index => $image) {
$imageName = time() . '_' . $index . '_' . $validated['floor_id'] . '_' . $validated['room_type'] . '.' . $image->getClientOriginalExtension();
$image->move(public_path('images/rooms'), $imageName);
$imagePaths[] = '/images/rooms/' . $imageName;
}
}

// Map floor_id to floor_name
$floorNames = [
Expand All @@ -48,15 +50,16 @@ public function store(Request $request)
'floor_name' => $floorName,
'floor_view' => $validated['floor_view'],
'floor_coords' => $floorCoords,
'room_number' => $validated['room_number'],
'room_name' => $validated['room_name'],
'room_type' => $validated['room_type'],
'price' => $validated['price'],
'description' => $validated['description'],
'image_url' => '/images/rooms/' . $imageName,
'facilities' => $request->input('facilities', []),
'image_url' => $imagePaths[0] ?? '',
'images' => $imagePaths,
'order' => $validated['order'],
]);

return redirect()->route('admin.rooms.index')->with('success', 'Room created successfully.');
return redirect()->route('admin.rooms.index')->with('success', 'Room category created successfully.');
}

/**
Expand All @@ -69,15 +72,14 @@ public function update(Request $request, $id)
$rules = [
'floor_id' => 'required|string',
'floor_view' => 'required|string|max:255',
'room_number' => 'required|integer',
'room_name' => 'required|string|max:255',
'room_type' => 'required|string|in:double,twin',
'price' => 'required|string|max:255',
'description' => 'required|string',
'order' => 'required|integer|min:0',
];

if ($request->hasFile('image')) {
$rules['image'] = 'image|max:5120';
if ($request->hasFile('images')) {
$rules['images.*'] = 'image|max:5120';
}

$validated = $request->validate($rules);
Expand All @@ -95,19 +97,33 @@ public function update(Request $request, $id)
'floor_id' => $validated['floor_id'],
'floor_name' => $floorName,
'floor_view' => $validated['floor_view'],
'room_number' => $validated['room_number'],
'room_name' => $validated['room_name'],
'room_type' => $validated['room_type'],
'price' => $validated['price'],
'description' => $validated['description'],
'facilities' => $request->input('facilities', []),
'order' => $validated['order'],
];

// Handle image upload
if ($request->hasFile('image')) {
$image = $request->file('image');
$imageName = time() . '_room_' . $validated['room_number'] . '.' . $image->getClientOriginalExtension();
$image->move(public_path('images/rooms'), $imageName);
$data['image_url'] = '/images/rooms/' . $imageName;
// Handle removed images
$currentImages = $room->images ?? [];
if ($request->has('removed_images')) {
$removedImages = json_decode($request->input('removed_images'), true) ?? [];
$currentImages = array_values(array_diff($currentImages, $removedImages));
}

// Handle new image uploads
if ($request->hasFile('images')) {
foreach ($request->file('images') as $index => $image) {
$imageName = time() . '_' . $index . '_' . $validated['floor_id'] . '_' . $validated['room_type'] . '.' . $image->getClientOriginalExtension();
$image->move(public_path('images/rooms'), $imageName);
$currentImages[] = '/images/rooms/' . $imageName;
}
}

// Update images array
if (!empty($currentImages)) {
$data['images'] = $currentImages;
$data['image_url'] = $currentImages[0]; // Keep first image as primary
}

$room->update($data);
Expand Down
10 changes: 8 additions & 2 deletions app/Models/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ class Room extends Model
'floor_name',
'floor_view',
'floor_coords',
'room_number',
'room_name',
'room_type',
'price',
'description',
'facilities',
'image_url',
'images',
'order',
];

protected $casts = [
'images' => 'array',
'facilities' => 'array',
];

protected static function boot()
{
parent::boot();
Expand Down
Loading
Loading