Added achievement data
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $internal_name
|
||||
* @property string $short_description
|
||||
* @property string $long_description
|
||||
* @property string $icon
|
||||
* @property bool $progressive
|
||||
* @property string|null $difficulty_description
|
||||
* @property int $achievement_category_id
|
||||
* @property int $achievement_difficulty_id
|
||||
*
|
||||
* @property-read AchievementCategory $category
|
||||
* @property-read AchievementDifficulty $difficulty
|
||||
* @property-read Collection<int, UserAchievement> $userAchievements
|
||||
* @property-read Collection<int, User> $users
|
||||
*/
|
||||
class Achievement extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'internal_name',
|
||||
'short_description',
|
||||
'long_description',
|
||||
'icon',
|
||||
'progressive',
|
||||
'difficulty_description',
|
||||
'achievement_category_id',
|
||||
'achievement_difficulty_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'progressive' => 'boolean',
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Relationships
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function category(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(AchievementCategory::class, 'achievement_category_id');
|
||||
}
|
||||
|
||||
public function difficulty(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(AchievementDifficulty::class, 'achievement_difficulty_id');
|
||||
}
|
||||
|
||||
public function userAchievements(): HasMany
|
||||
{
|
||||
return $this->hasMany(UserAchievement::class, 'achievement_id');
|
||||
}
|
||||
|
||||
/** Users who have unlocked (or are progressing toward) this achievement. */
|
||||
public function users(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'user_achievements')
|
||||
->withPivot('progress')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $internal_name
|
||||
* @property string $name
|
||||
* @property string $description
|
||||
*
|
||||
* @property-read Collection<int, Achievement> $achievements
|
||||
*/
|
||||
class AchievementCategory extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'internal_name',
|
||||
'name',
|
||||
'description',
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Relationships
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function achievements(): HasMany
|
||||
{
|
||||
return $this->hasMany(Achievement::class, 'achievement_category_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $internal_name
|
||||
* @property string $name
|
||||
* @property string $description
|
||||
*
|
||||
* @property-read Collection<int, Achievement> $achievements
|
||||
*/
|
||||
class AchievementDifficulty extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'internal_name',
|
||||
'name',
|
||||
'description',
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Relationships
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function achievements(): HasMany
|
||||
{
|
||||
return $this->hasMany(Achievement::class, 'achievement_difficulty_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $user_id
|
||||
* @property string $title
|
||||
* @property string $body
|
||||
* @property string|null $url
|
||||
* @property bool $is_achievement
|
||||
* @property int|null $achievement_id
|
||||
* @property \Carbon\Carbon|null $read_at
|
||||
* @property \Carbon\Carbon|null $dismissed_at
|
||||
* @property \Carbon\Carbon|null $expires_at
|
||||
*
|
||||
* @property-read User $user
|
||||
* @property-read Achievement|null $achievement
|
||||
*
|
||||
* @method static Builder unread()
|
||||
* @method static Builder undismissed()
|
||||
* @method static Builder active()
|
||||
* @method static Builder forAchievements()
|
||||
*/
|
||||
class Notification extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'title',
|
||||
'body',
|
||||
'url',
|
||||
'is_achievement',
|
||||
'achievement_id',
|
||||
'read_at',
|
||||
'expires_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_achievement' => 'boolean',
|
||||
'read_at' => 'datetime',
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Relationships
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function achievement(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Achievement::class);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Scopes
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
/** Notifications the user hasn't opened yet. */
|
||||
public function scopeUnread(Builder $query): void
|
||||
{
|
||||
$query->whereNull('read_at');
|
||||
}
|
||||
|
||||
/** Not expired (no expiry set, or expiry is in the future). */
|
||||
public function scopeActive(Builder $query): void
|
||||
{
|
||||
$query->where(function (Builder $q) {
|
||||
$q->whereNull('expires_at')
|
||||
->orWhere('expires_at', '>', now());
|
||||
});
|
||||
}
|
||||
|
||||
/** Only achievement notifications. */
|
||||
public function scopeForAchievements(Builder $query): void
|
||||
{
|
||||
$query->where('is_achievement', true);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function markAsRead(): void
|
||||
{
|
||||
if (! $this->read_at) {
|
||||
$this->update(['read_at' => now()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function isExpired(): bool
|
||||
{
|
||||
return $this->expires_at !== null && $this->expires_at->isPast();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $user_id
|
||||
* @property int $achievement_id
|
||||
* @property int|null $progress
|
||||
*
|
||||
* @property-read User $user
|
||||
* @property-read Achievement $achievement
|
||||
*/
|
||||
class UserAchievement extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'achievement_id',
|
||||
'progress',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'progress' => 'integer',
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Relationships
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function achievement(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Achievement::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,740 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// ---------------------------------------------------------------
|
||||
// 1. achievement_difficulties
|
||||
// ---------------------------------------------------------------
|
||||
Schema::create('achievement_difficulties', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('internal_name')->unique();
|
||||
$table->string('name');
|
||||
$table->text('description');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
DB::table('achievement_difficulties')->insert([
|
||||
[
|
||||
'internal_name' => 'easy',
|
||||
'name' => 'Easy',
|
||||
'description' => "As easy as booking a flight somewhere new!",
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'moderate',
|
||||
'name' => 'Moderate',
|
||||
'description' => 'You might have to reroute your flights a little just to get the achievement!',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'hard',
|
||||
'name' => 'Hard',
|
||||
'description' => "You'll need to go quite a bit out of the way to get this achievement!",
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'expensive',
|
||||
'name' => 'Expensive',
|
||||
'description' => 'It might be hard, but it will be easier if you have a lot of money!',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'near_impossible',
|
||||
'name' => 'Near-Impossible',
|
||||
'description' => 'You will actively have to try to get this achievement and they may be very few ways to go about it.',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'impossible',
|
||||
'name' => 'Impossible',
|
||||
'description' => 'This achievement is impossible to get if you start today, but was previously possible.',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
]);
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// 2. achievement_categories
|
||||
// ---------------------------------------------------------------
|
||||
Schema::create('achievement_categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('internal_name')->unique();
|
||||
$table->string('name');
|
||||
$table->text('description');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
DB::table('achievement_categories')->insert([
|
||||
[
|
||||
'internal_name' => 'general_flying',
|
||||
'name' => 'General Flying',
|
||||
'description' => 'Achievements earned through everyday flying activity.',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_and_continents',
|
||||
'name' => 'Countries & Continents',
|
||||
'description' => 'Achievements for visiting countries, regions, and continents.',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft',
|
||||
'name' => 'Aircraft',
|
||||
'description' => 'Achievements related to specific aircraft types and manufacturers.',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'airlines_and_alliances',
|
||||
'name' => 'Airlines and Alliances',
|
||||
'description' => 'Achievements focused on airlines and alliances',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'internal_name' => 'fun_challenges',
|
||||
'name' => 'Fun Challenges',
|
||||
'description' => 'Quirky and creative challenges for the adventurous traveller.',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
]);
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// 3. Alter achievements — add new columns & foreign keys
|
||||
// ---------------------------------------------------------------
|
||||
Schema::table('achievements', function (Blueprint $table) {
|
||||
// Replace description with split fields
|
||||
$table->string('short_description')->after('internal_name');
|
||||
$table->text('long_description')->after('short_description');
|
||||
|
||||
// Progressive tracking
|
||||
$table->boolean('progressive')->default(false)->after('long_description');
|
||||
|
||||
// Difficulty flavour text specific to this achievement
|
||||
$table->text('difficulty_description')->nullable()->after('progressive');
|
||||
|
||||
// Foreign keys
|
||||
$table->foreignId('achievement_category_id')
|
||||
->after('difficulty_description')
|
||||
->constrained('achievement_categories')
|
||||
->restrictOnDelete();
|
||||
|
||||
$table->foreignId('achievement_difficulty_id')
|
||||
->after('achievement_category_id')
|
||||
->constrained('achievement_difficulties')
|
||||
->restrictOnDelete();
|
||||
|
||||
// Drop the old monolithic description column
|
||||
$table->dropColumn('description');
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// 4. Alter user_achievements — add nullable progress column
|
||||
// ---------------------------------------------------------------
|
||||
Schema::table('user_achievements', function (Blueprint $table) {
|
||||
$table->unsignedInteger('progress')->nullable()->after('achievement_id');
|
||||
});
|
||||
|
||||
Schema::create('notifications', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId('user_id')
|
||||
->constrained('users')
|
||||
->cascadeOnDelete();
|
||||
|
||||
// Content
|
||||
$table->string('title');
|
||||
$table->text('body');
|
||||
$table->string('url')->nullable();
|
||||
|
||||
// Achievement flag & optional link
|
||||
$table->boolean('is_achievement')->default(false);
|
||||
$table->foreignId('achievement_id')
|
||||
->nullable()
|
||||
->constrained('achievements')
|
||||
->nullOnDelete();
|
||||
|
||||
// State
|
||||
$table->timestamp('read_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
// Common query patterns
|
||||
$table->index(['user_id', 'read_at']);
|
||||
$table->index('expires_at');
|
||||
});
|
||||
|
||||
|
||||
$this->seedAchievements();
|
||||
}
|
||||
|
||||
private function seedAchievements(): void
|
||||
{
|
||||
$difficulties = DB::table('achievement_difficulties')->pluck('id', 'internal_name');
|
||||
$categories = DB::table('achievement_categories')->pluck('id', 'internal_name');
|
||||
|
||||
$easy = $difficulties['easy'];
|
||||
$moderate = $difficulties['moderate'];
|
||||
$hard = $difficulties['hard'];
|
||||
$expensive = $difficulties['expensive'];
|
||||
$nearImposs = $difficulties['near_impossible'];
|
||||
$impossible = $difficulties['impossible'];
|
||||
|
||||
$generalFlying = $categories['general_flying'];
|
||||
$countriesAndContinents = $categories['countries_and_continents'];
|
||||
$aircraft = $categories['aircraft'];
|
||||
$airlinesAndAlliances = $categories['airlines_and_alliances'];
|
||||
$funChallenges = $categories['fun_challenges'];
|
||||
|
||||
$icon = 'standard_achievement.png';
|
||||
$now = now();
|
||||
|
||||
$achievements = [
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// General Flying
|
||||
// -----------------------------------------------------------
|
||||
[
|
||||
'internal_name' => 'general_flying.first_flight',
|
||||
'name' => 'Off the Ground',
|
||||
'short_description' => 'Log your very first flight.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.domestic_flight',
|
||||
'name' => 'Home Turf',
|
||||
'short_description' => 'Take a domestic flight.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.international_flight',
|
||||
'name' => 'Passport Stamp Collector',
|
||||
'short_description' => 'Take an international flight.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.business_class',
|
||||
'name' => 'Flatbed Fan',
|
||||
'short_description' => 'Fly in Business Class.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $expensive,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.first_class',
|
||||
'name' => 'Caviar at 35,000 Feet',
|
||||
'short_description' => 'Fly in First Class.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $expensive,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.premium_economy',
|
||||
'name' => 'Legroom Lover',
|
||||
'short_description' => 'Fly in Premium Economy.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $expensive,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.business_or_first',
|
||||
'name' => 'Turn Left',
|
||||
'short_description' => 'Fly in Business or First Class.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.fly_private',
|
||||
'name' => 'Wheels Up',
|
||||
'short_description' => 'Fly on a private aircraft.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $expensive,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.general_aviation',
|
||||
'name' => 'Little Plane, Big Sky',
|
||||
'short_description' => 'Take a general aviation flight.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.domestic_two_countries',
|
||||
'name' => 'Local Explorer',
|
||||
'short_description' => 'Take domestic flights in two different countries.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.10_flights',
|
||||
'name' => 'Frequent Flyer in Training',
|
||||
'short_description' => 'Log 10 flights.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.100_flights',
|
||||
'name' => 'Century in the Sky',
|
||||
'short_description' => 'Log 100 flights.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.500_flights',
|
||||
'name' => 'The 500 Club',
|
||||
'short_description' => 'Log 500 flights.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'general_flying.1000_flights',
|
||||
'name' => 'Skybound for Life',
|
||||
'short_description' => 'Log 1,000 flights.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $generalFlying,
|
||||
'achievement_difficulty_id'=> $expensive,
|
||||
],
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Countries & Continents
|
||||
// -----------------------------------------------------------
|
||||
[
|
||||
'internal_name' => 'countries_continents.intercontinental',
|
||||
'name' => 'Intercontinental',
|
||||
'short_description' => 'Fly between two different continents.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.fly_to_africa',
|
||||
'name' => 'Safari Bound',
|
||||
'short_description' => 'Fly to Africa.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.fly_to_asia',
|
||||
'name' => 'Orient Express (Air Edition)',
|
||||
'short_description' => 'Fly to Asia.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.fly_to_oceania',
|
||||
'name' => "G'Day from 35,000 Feet",
|
||||
'short_description' => 'Fly to Oceania.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.fly_to_antarctica',
|
||||
'name' => 'Penguin Spotter',
|
||||
'short_description' => 'Fly to Antarctica.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => 'Very few commercial or charter services operate to Antarctica, making this extremely difficult to pull off.',
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id'=> $expensive,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.fly_to_europe',
|
||||
'name' => 'Bonjour from Above',
|
||||
'short_description' => 'Fly to Europe.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.fly_to_south_america',
|
||||
'name' => 'Southern Cross',
|
||||
'short_description' => 'Fly to South America.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.fly_to_north_america',
|
||||
'name' => 'Stars, Stripes & Contrails',
|
||||
'short_description' => 'Fly to North America.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Aircraft
|
||||
// -----------------------------------------------------------
|
||||
[
|
||||
'internal_name' => 'aircraft.fly_on_a_plane',
|
||||
'name' => 'Up, Up and Away',
|
||||
'short_description' => 'Fly on an aeroplane.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.fly_on_a_helicopter',
|
||||
'name' => 'Chop Chop',
|
||||
'short_description' => 'Fly on a helicopter.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.fly_on_a_jet',
|
||||
'name' => 'Jet Setter',
|
||||
'short_description' => 'Fly on a jet-powered aircraft.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $easy,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.fly_on_a_prop',
|
||||
'name' => 'Propeller Head',
|
||||
'short_description' => 'Fly on a propeller driven aircraft.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.all_boeing_7x7',
|
||||
'name' => 'Seven Heaven',
|
||||
'short_description' => 'Fly every Boeing 7x7 aircraft family.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => 'Requires flying the 707, 717, 727, 737, 747, 757, 767, 777, and 787 — some of which are no longer in commercial service.',
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $impossible,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.all_airbus_a3xx',
|
||||
'name' => 'Airbus Aristocrat',
|
||||
'short_description' => 'Fly every Airbus A3xx aircraft family.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => 'Covers the A300, A310, A318–A321, A330, A340, A350, and A380 families. You are likely going to have to go to Iran to get this one.',
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $nearImposs,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.quad_engine',
|
||||
'name' => 'Four on the Floor',
|
||||
'short_description' => 'Fly on a four-engine aircraft.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => 'Four-engine jets are becoming increasingly rare as twin-engine widebodies dominate long-haul routes.',
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.double_decker',
|
||||
'name' => 'Upper Deck Club',
|
||||
'short_description' => 'Fly on a double-decker aircraft.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => 'Primarily the A380 and 747, which operate on a limited and shrinking number of routes.',
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.single_engine',
|
||||
'name' => 'Solo Spinner',
|
||||
'short_description' => 'Fly on a single-engine aircraft.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.tri_engine',
|
||||
'name' => 'Triple Threat',
|
||||
'short_description' => 'Fly on a tri-engine aircraft.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => 'Most tri-jets are out of service nowadays, and tri-props even rarer',
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $nearImposs,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'aircraft.smaller_manufacturer',
|
||||
'name' => 'Break the Duopoly',
|
||||
'short_description' => 'Fly on an aircraft from a manufacturer other than Boeing or Airbus.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => false,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $aircraft,
|
||||
'achievement_difficulty_id'=> $moderate,
|
||||
],
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Airlines & Alliances
|
||||
// -----------------------------------------------------------
|
||||
[
|
||||
'internal_name' => 'airlines_alliances.all_skyteam',
|
||||
'name' => 'Team Player',
|
||||
'short_description' => 'Fly with every airline in the SkyTeam alliance.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $airlinesAndAlliances,
|
||||
'achievement_difficulty_id'=> $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'airlines_alliances.all_oneworld',
|
||||
'name' => 'One World Wonder',
|
||||
'short_description' => 'Fly with every airline in the Oneworld alliance.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $airlinesAndAlliances,
|
||||
'achievement_difficulty_id'=> $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'airlines_alliances.all_star_alliance',
|
||||
'name' => 'Star Collector',
|
||||
'short_description' => 'Fly with every airline in the Star Alliance.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $airlinesAndAlliances,
|
||||
'achievement_difficulty_id'=> $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'airlines_alliances.all_vanilla_alliance',
|
||||
'name' => 'Plain Extraordinary',
|
||||
'short_description' => 'Fly with every airline in the Vanilla Alliance.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $airlinesAndAlliances,
|
||||
'achievement_difficulty_id'=> $hard,
|
||||
],
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Fun Challenges
|
||||
// ---------------------------------------------------------------
|
||||
[
|
||||
'internal_name' => 'fun_challenges.airline_alphabet',
|
||||
'name' => 'Fly the Alphabet',
|
||||
'short_description' => 'Fly with an airline whose IATA code starts with every letter of the alphabet.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => 'Some letters have very few airlines with a matching IATA code, requiring creative routing.',
|
||||
'achievement_category_id' => $funChallenges,
|
||||
'achievement_difficulty_id' => $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'fun_challenges.airport_alphabet',
|
||||
'name' => 'Visit the Alphabet',
|
||||
'short_description' => 'Visit an airport whose IATA code starts with every letter of the alphabet.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => 'Certain letters — particularly Q, X, and Z — have extremely limited airport coverage, making this a serious challenge.',
|
||||
'achievement_category_id' => $funChallenges,
|
||||
'achievement_difficulty_id' => $nearImposs,
|
||||
],
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Countries & Continents
|
||||
// ---------------------------------------------------------------
|
||||
[
|
||||
'internal_name' => 'countries_continents.all_inhabited_continents',
|
||||
'name' => 'Six of One',
|
||||
'short_description' => 'Fly to all six inhabited continents.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => null,
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id' => $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.all_continents',
|
||||
'name' => 'Seven Wonders of the Skies',
|
||||
'short_description' => 'Fly to all seven continents, including Antarctica.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => 'Antarctica has no commercial scheduled service — expect a specialist charter or expedition flight.',
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id' => $nearImposs,
|
||||
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.all_continent_pairs_one_way',
|
||||
'name' => 'Grand Junction',
|
||||
'short_description' => 'Fly between every possible pair of continents at least one way.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => 'There are 15 unique continent pairs (excluding Antarctica)',
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id' => $hard,
|
||||
],
|
||||
[
|
||||
'internal_name' => 'countries_continents.all_continent_pairs_both_ways',
|
||||
'name' => 'There and Back Again',
|
||||
'short_description' => 'Fly between every possible pair of continents in both directions.',
|
||||
'long_description' => '',
|
||||
'icon' => $icon,
|
||||
'progressive' => true,
|
||||
'difficulty_description' => "30 different intercontinental flights required, good luck.",
|
||||
'achievement_category_id' => $countriesAndContinents,
|
||||
'achievement_difficulty_id' => $nearImposs,
|
||||
],
|
||||
|
||||
|
||||
];
|
||||
|
||||
DB::table('achievements')->insert($achievements);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
// Reverse user_achievements
|
||||
Schema::table('user_achievements', function (Blueprint $table) {
|
||||
$table->dropColumn('progress');
|
||||
});
|
||||
|
||||
// Reverse achievements
|
||||
Schema::table('achievements', function (Blueprint $table) {
|
||||
$table->text('description')->after('internal_name');
|
||||
$table->dropForeign(['achievement_difficulty_id']);
|
||||
$table->dropForeign(['achievement_category_id']);
|
||||
$table->dropColumn([
|
||||
'short_description',
|
||||
'long_description',
|
||||
'progressive',
|
||||
'difficulty_description',
|
||||
'achievement_category_id',
|
||||
'achievement_difficulty_id',
|
||||
]);
|
||||
});
|
||||
|
||||
Schema::dropIfExists('achievement_categories');
|
||||
Schema::dropIfExists('achievement_difficulties');
|
||||
Schema::dropIfExists('notifications');
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user