Files
2026-04-28 16:10:27 +03:00

530 lines
16 KiB
Markdown

### Project Idea
This is a travel app based on wikipedia informations.
The user can choose a city, we show the city on the map with some info about it (from wikipedia, like city summary) and show things to do there.
We need to store in mysql data from wikipedia, opentripmap and overpass for cache.
---
## Technical Architecture
### Tech Stack
**Frontend:**
- Alpine.js for interactive UI components
- Tailwind CSS for styling (or Bootstrap 5)
- Leaflet.js for interactive maps
- Laravel Livewire (optional) for dynamic components
- Blade templating engine
**Backend:**
- Laravel 10/11 (PHP)
- MySQL database for caching API responses
- Redis for caching and queue management
- Laravel HTTP Client (Guzzle) for API requests
- Laravel Cache for API response caching
- Laravel Jobs/Queues for background API fetching
**External APIs:**
- Wikipedia API - City summaries and information
- OpenTripMap API - Points of interest and attractions
- Overpass API (OpenStreetMap) - Geographic data and additional POIs
---
## Database Schema
### Migrations
**cities** (create_cities_table.php)
```php
$table->id();
$table->string('name');
$table->string('country');
$table->decimal('latitude', 10, 7);
$table->decimal('longitude', 10, 7);
$table->text('wikipedia_summary')->nullable();
$table->string('wikipedia_url')->nullable();
$table->string('wikipedia_page_id')->nullable();
$table->timestamps();
$table->timestamp('wikipedia_cached_at')->nullable();
// Indexes
$table->index(['name', 'country']);
$table->index('wikipedia_cached_at');
```
**attractions** (create_attractions_table.php)
```php
$table->id();
$table->foreignId('city_id')->constrained()->onDelete('cascade');
$table->string('name');
$table->text('description')->nullable();
$table->decimal('latitude', 10, 7);
$table->decimal('longitude', 10, 7);
$table->string('category')->nullable();
$table->enum('source', ['opentripmap', 'overpass', 'wikipedia']);
$table->string('source_id')->nullable();
$table->decimal('rating', 3, 2)->nullable();
$table->string('image_url')->nullable();
$table->timestamps();
$table->timestamp('cached_at')->nullable();
// Indexes
$table->index('city_id');
$table->index('category');
$table->index('cached_at');
```
**users** (Laravel's default users table with modifications)
```php
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
```
**favorites** (create_favorites_table.php)
```php
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('city_id')->constrained()->onDelete('cascade');
$table->timestamps();
// Indexes
$table->unique(['user_id', 'city_id']); // Prevent duplicate favorites
$table->index('user_id');
```
**trips** (create_trips_table.php)
```php
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('name');
$table->text('description')->nullable();
$table->timestamps();
// Indexes
$table->index('user_id');
```
**city_trip** (create_city_trip_table.php) - Pivot table
```php
$table->id();
$table->foreignId('trip_id')->constrained()->onDelete('cascade');
$table->foreignId('city_id')->constrained()->onDelete('cascade');
$table->integer('order')->default(0); // Order of cities in trip
$table->timestamps();
// Indexes
$table->index(['trip_id', 'city_id']);
```
**personal_access_tokens** (Laravel Sanctum - auto-generated)
- Handles API tokens for mobile app authentication
**Note:** Laravel's built-in caching system (Redis/Database) will handle cache metadata, so a separate cache_metadata table is optional.
---
## Routes Structure
### Web Routes (resources/routes/web.php)
**GET /**
- Home page with city search
**GET /cities/{city}**
- City details page with map and attractions
- Uses Blade view with Alpine.js for interactivity
### API Routes (resources/routes/api.php)
**Authentication Routes (Laravel Sanctum)**
**POST /api/register**
- Register new user account
- Returns user data and API token
**POST /api/login**
- Login with email and password
- Returns user data and API token
**POST /api/logout** (Protected)
- Logout and revoke current token
- Requires: Bearer token
**GET /api/user** (Protected)
- Get authenticated user profile
- Requires: Bearer token
**Public API Routes**
**GET /api/cities/search?q={query}**
- Search for cities by name
- Returns JSON list of matching cities
- Public access (no auth required)
**GET /api/cities/{cityId}**
- Get detailed information about a specific city
- Returns city data from cache or fetches from Wikipedia API
- Public access
**GET /api/cities/{cityId}/attractions**
- Get all attractions for a specific city
- Returns cached data or fetches from OpenTripMap and Overpass APIs
- Optional query params: ?category={category}&limit={number}
- Public access
**GET /api/cities/{cityId}/map-data**
- Get geographic data for map rendering
- Returns coordinates and boundaries for Leaflet
- Public access
**Protected API Routes (Require Authentication)**
**POST /api/cities** (Protected)
- Add a new city to the database
- Dispatches job to fetch data from all APIs
- Requires: Bearer token
**GET /api/favorites** (Protected)
- Get user's favorite cities
- Returns list of saved cities
**POST /api/favorites/{cityId}** (Protected)
- Add city to user favorites
- Returns updated favorites list
**DELETE /api/favorites/{cityId}** (Protected)
- Remove city from user favorites
- Returns success status
**GET /api/trips** (Protected)
- Get user's saved trips/itineraries
- Returns list of trips with cities
**POST /api/trips** (Protected)
- Create new trip/itinerary
- Body: {name, description, city_ids[]}
**PUT /api/trips/{tripId}** (Protected)
- Update existing trip
- Body: {name, description, city_ids[]}
**DELETE /api/trips/{tripId}** (Protected)
- Delete trip
- Returns success status
---
## Core Features
### 1. City Search
- Autocomplete search functionality
- Search by city name
- Display search results with country and preview info
### 2. City Details Page
- Display city name and country
- Show Wikipedia summary (first 2-3 paragraphs)
- Link to full Wikipedia article
- Display city location on interactive map
### 3. Interactive Map
- Centered on selected city
- Markers for attractions/points of interest
- Color-coded markers by category
- Click markers to see attraction details
- Zoom and pan controls
### 4. Attractions List
- Categorized list of things to do
- Filter by category (museums, restaurants, parks, etc.)
- Sort by distance, rating, or name
- Click to highlight on map
### 5. Caching System
- Cache Wikipedia data for 30 days
- Cache OpenTripMap data for 7 days
- Cache Overpass data for 7 days
- Automatic cache invalidation and refresh
---
## Implementation Steps
### Phase 1: Laravel Project Setup
1. Initialize Laravel project: `composer create-project laravel/laravel wiki_travel_site`
2. Install Laravel Sanctum for API authentication: `composer require laravel/sanctum`
3. Publish Sanctum config: `php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"`
4. Install Tailwind CSS: `npm install -D tailwindcss postcss autoprefixer && npx tailwindcss init`
5. Configure MySQL database connection in `.env`
6. Set up Redis for caching
7. Create database migrations for cities, attractions, users, favorites, trips
8. Run migrations: `php artisan migrate`
9. Configure CORS for mobile app in `config/cors.php`
### Phase 2: Models & Services
1. Create Eloquent models: `City`, `Attraction`, `Trip`, `Favorite`
2. Define model relationships:
- City hasMany Attractions
- User hasMany Favorites
- User hasMany Trips
- Trip belongsToMany Cities (pivot: city_trip)
- City belongsToMany Trips
3. Add HasApiTokens trait to User model for Sanctum
4. Create service classes:
- `WikipediaService` - Fetch city data from Wikipedia API
- `OpenTripMapService` - Fetch attractions from
- OpenTripMap API
- `OverpassService` - Fetch geographic data from Overpass API
5. Implement Laravel Cache facade for API response caching
6. Create Jobs for background API fetching:
- `FetchCityDataJob`
- `FetchAttractionsJob`
### Phase 3: Authentication & Controllers
1. Create `AuthController` for registration/login/logout
2. Create `CityController` for web routes
3. Create API controllers:
- `API\CityController` - City and attraction endpoints
- `API\FavoriteController` - Favorite management
- `API\TripController` - Trip/itinerary management
- `API\UserController` - User profile management
4. Define web routes in `routes/web.php`
5. Define API routes in `routes/api.php` with Sanctum middleware
6. Implement API resource classes for JSON responses:
- `CityResource`
- `AttractionResource`
- `TripResource`
- `UserResource`
7. Create Form Request validators for registration and trip creation
### Phase 4: Frontend Views
1. Create Blade layouts:
- `layouts/app.blade.php` - Main layout with Tailwind/Bootstrap
- `welcome.blade.php` - Homepage with search
- `cities/show.blade.php` - City details page
2. Install and configure Alpine.js
3. Integrate Leaflet.js for maps
4. Create Alpine.js components:
- City search with autocomplete
- Interactive map with markers
- Attractions filter and list
5. Style with Tailwind CSS utility classes
### Phase 5: Integration & Caching
1. Implement cache logic in services (30 days for Wikipedia, 7 days for attractions)
2. Configure Redis cache driver in `config/cache.php`
3. Add error handling and logging
4. Create artisan commands:
- `php artisan cache:clear-cities` - Clear city cache
- `php artisan attractions:refresh {cityId}` - Refresh attraction data
5. Test all API integrations
### Phase 6: Polish & Optimization
1. Add loading states with Alpine.js
2. Implement responsive design (mobile-first with Tailwind)
3. Optimize database queries (eager loading, indexing)
4. Add pagination for attractions list
5. Implement search history using Laravel sessions
6. Write feature tests with PHPUnit
7. Deploy to production (Laravel Forge, Vapor, or custom server)
---
## Data Flow
1. User searches for a city
2. Frontend sends request to backend
3. Backend checks MySQL cache:
- If cached and not expired: return cached data
- If not cached or expired: fetch from Wikipedia API, store in cache, return data
4. User selects a city
5. Backend fetches attractions:
- Check cache for OpenTripMap and Overpass data
- If expired, fetch fresh data and update cache
- Merge data from multiple sources
6. Frontend displays city info on map with markers
7. User interacts with map and attraction list
---
## Environment Variables (.env)
```
APP_NAME="Wiki Travel Site"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=wiki_travel_site
DB_USERNAME=root
DB_PASSWORD=
# Redis Cache
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# API Keys
OPENTRIPMAP_API_KEY=your_key_here
WIKIPEDIA_API_URL=https://en.wikipedia.org/api/rest_v1
OVERPASS_API_URL=https://overpass-api.de/api/interpreter
# Cache Settings (in days)
CACHE_WIKIPEDIA_TTL=30
CACHE_ATTRACTIONS_TTL=7
```
---
## Laravel-Specific Features to Leverage
- **Eloquent ORM** - For database interactions and relationships
- **Laravel Cache** - Redis-backed caching for API responses
- **Laravel Jobs/Queues** - Background processing for API requests
- **Artisan Commands** - Custom commands for cache management
- **API Resources** - Transform Eloquent models to JSON
- **Form Requests** - Validate incoming city search requests
- **Laravel Mix/Vite** - Asset compilation for Tailwind CSS and Alpine.js
- **Blade Components** - Reusable UI components (search bar, attraction card, etc.)
- **Laravel Sanctum** - API authentication (for future mobile app)
---
## Project Structure
```
wiki_travel_site/
├── app/
│ ├── Http/
│ │ ├── Controllers/
│ │ │ ├── CityController.php
│ │ │ ├── AuthController.php
│ │ │ └── API/
│ │ │ ├── CityController.php
│ │ │ ├── FavoriteController.php
│ │ │ ├── TripController.php
│ │ │ └── UserController.php
│ │ ├── Resources/
│ │ │ ├── CityResource.php
│ │ │ ├── AttractionResource.php
│ │ │ ├── TripResource.php
│ │ │ └── UserResource.php
│ │ ├── Requests/
│ │ │ ├── RegisterRequest.php
│ │ │ ├── LoginRequest.php
│ │ │ └── TripRequest.php
│ │ └── Middleware/
│ │ └── EnsureTokenIsValid.php
│ ├── Models/
│ │ ├── User.php (with HasApiTokens trait)
│ │ ├── City.php
│ │ ├── Attraction.php
│ │ ├── Trip.php
│ │ └── Favorite.php
│ ├── Services/
│ │ ├── WikipediaService.php
│ │ ├── OpenTripMapService.php
│ │ └── OverpassService.php
│ └── Jobs/
│ ├── FetchCityDataJob.php
│ └── FetchAttractionsJob.php
├── database/
│ └── migrations/
│ ├── create_users_table.php
│ ├── create_cities_table.php
│ ├── create_attractions_table.php
│ ├── create_favorites_table.php
│ ├── create_trips_table.php
│ ├── create_city_trip_table.php
│ └── create_personal_access_tokens_table.php (Sanctum)
├── resources/
│ ├── views/
│ │ ├── layouts/
│ │ │ └── app.blade.php
│ │ ├── auth/
│ │ │ ├── login.blade.php
│ │ │ └── register.blade.php
│ │ ├── welcome.blade.php
│ │ └── cities/
│ │ └── show.blade.php
│ ├── css/
│ │ └── app.css (Tailwind)
│ └── js/
│ └── app.js (Alpine.js)
├── routes/
│ ├── web.php
│ └── api.php (with Sanctum auth middleware)
└── config/
├── cache.php
├── sanctum.php
├── cors.php (configured for mobile app)
└── services.php (API keys)
```
---
## Mobile App Integration
The backend API is designed to support a mobile application using Laravel Sanctum for token-based authentication.
### Mobile App Authentication Flow
1. **Registration:** POST /api/register → Returns user + token
2. **Login:** POST /api/login → Returns user + token
3. **Authenticated Requests:** Include token in header: `Authorization: Bearer {token}`
4. **Logout:** POST /api/logout → Revokes token
### Mobile App Features
- Browse and search cities (no auth required)
- View city details and attractions on map
- User registration and login
- Save favorite cities
- Create and manage trip itineraries
- Sync favorites and trips across devices
- Offline caching (handle on mobile side)
### CORS Configuration
Configure `config/cors.php` to allow requests from mobile app:
```php
'paths' => ['api/*'],
'allowed_origins' => ['*'], // Or specify mobile app origins
'allowed_methods' => ['*'],
'allowed_headers' => ['*'],
'supports_credentials' => false,
```
---
## Future Enhancements
- Weather information integration
- User reviews and ratings with Laravel policies
- Photo galleries from Wikimedia Commons
- Nearby restaurants and hotels
- Public transportation information
- Push notifications for trip reminders (mobile app)
- Social sharing of trips
- Multi-language support with Laravel localization
- Laravel Scout for full-text search
- Telescope for debugging during development
- Rate limiting for API endpoints
- Email verification for user accounts
- Password reset functionality
- User profile pictures and customization