first commit
This commit is contained in:
@@ -0,0 +1,529 @@
|
||||
### 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
|
||||
Reference in New Issue
Block a user