*/ protected $fillable = [ 'name', 'slug', 'website', 'description', 'industry_sector', 'founded_year', 'employee_count', 'annual_revenue', 'market_cap', 'stock_symbol', 'headquarters_location', 'geographic_presence', 'company_type', 'parent_company_id', 'is_public', 'is_active', 'total_parks', 'total_rides_manufactured', 'total_rides_designed', ]; /** * The attributes that should be cast. * * @var array */ protected $casts = [ 'founded_year' => 'integer', 'employee_count' => 'integer', 'annual_revenue' => 'decimal:2', 'market_cap' => 'decimal:2', 'is_public' => 'boolean', 'is_active' => 'boolean', 'total_parks' => 'integer', 'total_rides_manufactured' => 'integer', 'total_rides_designed' => 'integer', ]; /** * Get the parks operated by this operator. */ public function parks(): HasMany { return $this->hasMany(Park::class); } /** * Get the rides manufactured by this operator. */ public function manufactured_rides(): HasMany { return $this->hasMany(Ride::class, 'manufacturer_id'); } /** * Get the rides designed by this operator. */ public function designed_rides(): HasMany { return $this->hasMany(Ride::class, 'designer_id'); } /** * Get the parent company if this is a subsidiary. */ public function parent_company(): BelongsTo { return $this->belongsTo(Operator::class, 'parent_company_id'); } /** * Get the subsidiary companies. */ public function subsidiaries(): HasMany { return $this->hasMany(Operator::class, 'parent_company_id'); } /** * Update comprehensive statistics. */ public function updateStatistics(): void { $this->total_parks = $this->parks()->count(); $this->total_rides_manufactured = $this->manufactured_rides()->count(); $this->total_rides_designed = $this->designed_rides()->count(); $this->save(); } /** * Get the operator's display name with role indicators. */ public function getDisplayNameAttribute(): string { $roles = []; if ($this->total_parks > 0) $roles[] = 'Operator'; if ($this->total_rides_manufactured > 0) $roles[] = 'Manufacturer'; if ($this->total_rides_designed > 0) $roles[] = 'Designer'; $roleText = empty($roles) ? '' : ' (' . implode(', ', $roles) . ')'; return $this->name . $roleText; } /** * Get formatted website URL (ensures proper URL format). */ public function getWebsiteUrlAttribute(): string { if (!$this->website) { return ''; } $website = $this->website; if (!str_starts_with($website, 'http://') && !str_starts_with($website, 'https://')) { $website = 'https://' . $website; } return $website; } /** * Get company size category based on employee count. */ public function getCompanySizeCategoryAttribute(): string { if (!$this->employee_count) return 'unknown'; return match (true) { $this->employee_count <= 100 => 'small', $this->employee_count <= 1000 => 'medium', $this->employee_count <= 10000 => 'large', default => 'enterprise' }; } /** * Get geographic presence level. */ public function getGeographicPresenceLevelAttribute(): string { $countries = $this->parks() ->join('locations', 'parks.location_id', '=', 'locations.id') ->distinct('locations.country') ->count('locations.country'); return match (true) { $countries <= 1 => 'regional', $countries <= 3 => 'national', default => 'international' }; } /** * Get market influence score. */ public function getMarketInfluenceScoreAttribute(): float { $score = 0; // Parks operated (40% weight) $score += min($this->total_parks * 10, 40); // Rides manufactured (30% weight) $score += min($this->total_rides_manufactured * 2, 30); // Revenue influence (20% weight) if ($this->annual_revenue) { $score += min(($this->annual_revenue / 1000000000) * 10, 20); } // Geographic presence (10% weight) $countries = $this->parks() ->join('locations', 'parks.location_id', '=', 'locations.id') ->distinct('locations.country') ->count('locations.country'); $score += min($countries * 2, 10); return round($score, 1); } /** * Scope a query to only include active operators. */ public function scopeActive($query) { return $query->where('is_active', true); } /** * Scope a query to only include park operators. */ public function scopeParkOperators($query) { return $query->whereHas('parks'); } /** * Scope a query to only include ride manufacturers. */ public function scopeManufacturers($query) { return $query->whereHas('manufactured_rides'); } /** * Scope a query to only include ride designers. */ public function scopeDesigners($query) { return $query->whereHas('designed_rides'); } /** * Scope a query to only include major operators (with multiple parks). */ public function scopeMajorOperators($query, int $minParks = 3) { return $query->where('total_parks', '>=', $minParks); } /** * Scope a query to filter by industry sector. */ public function scopeIndustrySector($query, string $sector) { return $query->where('industry_sector', $sector); } /** * Scope a query to filter by company size. */ public function scopeCompanySize($query, string $size) { $ranges = [ 'small' => [1, 100], 'medium' => [101, 1000], 'large' => [1001, 10000], 'enterprise' => [10001, PHP_INT_MAX] ]; if (isset($ranges[$size])) { return $query->whereBetween('employee_count', $ranges[$size]); } return $query; } /** * Scope a query to filter by founded year range. */ public function scopeFoundedBetween($query, int $startYear, int $endYear) { return $query->whereBetween('founded_year', [$startYear, $endYear]); } /** * Scope a query to filter by revenue range. */ public function scopeRevenueBetween($query, float $minRevenue, float $maxRevenue) { return $query->whereBetween('annual_revenue', [$minRevenue, $maxRevenue]); } /** * Scope a query to filter by dual roles. */ public function scopeDualRole($query, array $roles) { return $query->where(function ($q) use ($roles) { if (in_array('park_operator', $roles)) { $q->orWhereHas('parks'); } if (in_array('ride_manufacturer', $roles)) { $q->orWhereHas('manufactured_rides'); } if (in_array('ride_designer', $roles)) { $q->orWhereHas('designed_rides'); } }); } /** * Scope a query for optimized loading with counts. */ public function scopeWithCounts($query) { return $query->withCount(['parks', 'manufactured_rides', 'designed_rides']); } /** * Get the route key for the model. */ public function getRouteKeyName(): string { return 'slug'; } }