Overview
Heimdinger.lol aggregates League of Legends data from multiple sources to provide comprehensive champion, skin, and asset information. The application implements a robust fallback system to ensure data availability.
Data Source Priority
Boris API (Primary)
Custom internal API providing processed champion data
Meraki Analytics (Fallback)
Community-maintained CDN with structured LoL data
Community Dragon (Assets)
High-quality champion images and ability icons
Boris Static Data Client
The BorisStaticDataClient service is the primary interface for fetching champion and rate data with automatic fallback handling.
Service Location
app / Services / BorisStaticDataClient . php
Configuration
Boris API credentials are configured in config/services.php:
'boris' => [
'url' => env ( 'BORIS_URL' , 'https://boris.heimerdinger.lol' ),
'api_key' => env ( 'BORIS_API_KEY' ),
],
Set BORIS_URL and BORIS_API_KEY in your .env file to configure the connection.
Data Fetching Methods
Get Champions Data
Retrieves all champion information with automatic fallback:
app/Services/BorisStaticDataClient.php:21
public function getChampions () : array
{
$payload = $this -> fetchWithFallback (
self :: CHAMPIONS_ENDPOINT ,
self :: MERAKI_CHAMPIONS_URL ,
fn ( mixed $payload ) : bool => $this -> isChampionPayload ( $payload )
);
return $this -> normalizeChampionPayload ( $payload );
}
Endpoints:
Primary : https://boris.heimerdinger.lol/lolstaticdata/champions.json
Fallback : https://cdn.merakianalytics.com/riot/lol/resources/latest/en-US/champions.json
Get Champion Rates
Fetches win rates, pick rates, and ban rates:
app/Services/BorisStaticDataClient.php:32
public function getChampionRates () : array
{
return $this -> fetchWithFallback (
self :: CHAMPION_RATES_ENDPOINT ,
self :: MERAKI_CHAMPION_RATES_URL ,
fn ( mixed $payload ) : bool => $this -> isChampionRatesPayload ( $payload )
);
}
Endpoints:
Primary : https://boris.heimerdinger.lol/lolstaticdata/championrates.json
Fallback : https://cdn.merakianalytics.com/riot/lol/resources/latest/en-US/championrates.json
Fallback System
The service implements a sophisticated fallback mechanism to ensure data availability:
Attempt Boris API
Make request to Boris API with authentication: app/Services/BorisStaticDataClient.php:109
$response = Http :: withHeaders ([
'X-API-Key' => ( string ) config ( 'services.boris.api_key' ),
]) -> get ( $this -> borisUrl () . $endpoint );
Validate Payload
Verify the response matches expected data structure: app/Services/BorisStaticDataClient.php:141
private function isChampionPayload ( mixed $payload ) : bool
{
if ( ! is_array ( $payload ) || $payload === []) {
return false ;
}
$firstChampion = reset ( $payload );
return is_array ( $firstChampion ) && isset ( $firstChampion [ 'id' ]);
}
Fall Back to Meraki
If Boris fails or returns invalid data, fetch from Meraki: app/Services/BorisStaticDataClient.php:66
$payload = $this -> fetchFromMeraki ( $merakiUrl );
if ( $validator ( $payload )) {
Log :: warning ( 'Using Meraki static data fallback.' );
return $payload ;
}
Throw Exception
If both sources fail, throw a descriptive exception: app/Services/BorisStaticDataClient.php:88
throw new RuntimeException (
sprintf ( 'Unable to fetch static data from Boris or Meraki for [%s].' , $borisEndpoint )
);
Error Logging
All failures are logged with context for debugging:
app/Services/BorisStaticDataClient.php:52
Log :: warning ( 'Boris static data request failed.' , [
'source' => 'boris' ,
'endpoint' => $borisEndpoint ,
'status' => $exception -> getCode () ?: null ,
'message' => $exception -> getMessage (),
]);
Monitor your logs for frequent fallbacks to Meraki, which may indicate Boris API issues.
Community Dragon provides high-quality champion assets directly from game files.
Champion Images
Images are accessed via model accessors:
app/Models/Champion.php:108
public function getChampionSquareImageAttribute () : string
{
return 'https://raw.communitydragon.org/pbe/plugins/rcp-be-lol-game-data/global/default/v1/champion-icons/'
. $this -> champion_id . '.png' ;
}
Ability Icons
Each ability has a dedicated accessor:
Q Ability
W Ability
Passive
public function getChampionAbilityIconQAttribute () : string
{
return 'https://cdn.communitydragon.org/latest/champion/'
. $this -> champion_id . '/ability-icon/q' ;
}
Usage in Views
< img src = " {{ $champion -> champion_square_image }} " alt = " {{ $champion -> name }} " >
< img src = " {{ $champion -> champion_ability_icon_q }} " alt = "Q Ability" >
Data Normalization
The service normalizes data structures between sources:
app/Services/BorisStaticDataClient.php:176
private function normalizeChampionPayload ( array $payload ) : array
{
// Meraki returns keyed array, Boris returns list
if ( array_is_list ( $payload )) {
return $payload ;
}
return array_values ( $payload );
}
This ensures consistent data structure regardless of source.
Payload Validation
Champion Payload
Validates champion data structure:
app/Services/BorisStaticDataClient.php:141
private function isChampionPayload ( mixed $payload ) : bool
{
if ( ! is_array ( $payload ) || $payload === []) {
return false ;
}
if ( array_is_list ( $payload )) {
return isset ( $payload [ 0 ][ 'id' ]);
}
$firstChampion = reset ( $payload );
return is_array ( $firstChampion ) && isset ( $firstChampion [ 'id' ]);
}
Champion Rates Payload
Validates rate data structure:
app/Services/BorisStaticDataClient.php:156
private function isChampionRatesPayload ( mixed $payload ) : bool
{
return is_array ( $payload ) && isset ( $payload [ 'data' ]) && is_array ( $payload [ 'data' ]);
}
Environment Configuration
Required environment variables for data sources:
BORIS_URL = https://boris.heimerdinger.lol
BORIS_API_KEY = your-api-key-here
# Optional: Riot API for additional features
RGAPI_KEY = "RGAPI-00000000-0000-0000-0000-000000000000"
USER_AGENT = "Heimerdinger/1.0 (Heimerdinger.lol) PHP"
The Riot API key (RGAPI_KEY) is optional for core functionality but required for live sale rotation data.
Best Practices
Always cache API responses to reduce external requests: $champions = Cache :: remember ( 'champions_data' , 3600 , function () {
return app ( BorisStaticDataClient :: class ) -> getChampions ();
});
Wrap data fetching in try-catch blocks: try {
$champions = $client -> getChampions ();
} catch ( RuntimeException $e ) {
Log :: error ( 'Failed to fetch champions' , [ 'error' => $e -> getMessage ()]);
// Return cached data or show error message
}
Respect API rate limits by implementing appropriate caching and request throttling.
Testing Data Sources
Test the Boris client in Tinker:
$client = app ( \App\Services\ BorisStaticDataClient :: class );
$champions = $client -> getChampions ();
count ( $champions ); // Should return ~160+ champions
Next Steps
Architecture Understand the overall application architecture
Artisan Commands Learn about available CLI commands for data management