Okay, this post might get huge as i am going to provide complete guild member update process here
try {
UpdateSingleCharacter::complete(
$this->region,
$this->realm,
$member->name()
);
} catch (ResourceAccessForbidden $exception) {
// Do nothing as for some reason 403 is returned
}
This is the UpdateSingleCharacter class and the complete method which is responsible of updating all character data (items, specs, guild, etc)
public static function complete(string $region, string $realm, string $name) : void {
$client = (new Client)->setRegion($region); // Initialize Battle.Net client with Credentials flow
$character= $client->profile()->character($realm, $name); // Set the character information we are working with
$models = (new CharacterCompleteProfileToModel($character->complete()))->models(); // Load all character data into respective Laravel Models
foreach ($models as $modelClass => $modelData) {
if (Arr::has($modelData, 'lookup')) { // 'Lookup' here is only set if the character already exists in the database
self::executeQuery($modelClass, $modelData);
} else {
// This piece of code will only run if it is a new character
foreach ($modelData as $value) {
self::executeQuery($modelClass, $value);
}
}
}
}
CharacterCompleteProfileToModel class constructor
public function __construct(CharacterEntity $character) {
$this->character = $character;
$this
->generateCharacterModelData() // This is character data model (id, name, gender, faction, race, etc)
->generateCharacterRealmData() // This is character realm data model
->generateCharacterGuildData() // This is character guild data model
->generateCharacterSpecializationsData()
->generateCharacterEquipmentData()
->generateCharacterProgressionData();
}
CharacterCompleteProfileToModel()->generateCharacterGuildData
This is where i am checking if character has guild attached, if it does not, then we have a problem as there is no reason guild member should not have guild attached to it
private function generateCharacterGuildData() : self {
$guild = $this->character->guild();
if ($guild !== null) {
$this->models[CharacterGuildModel::class] = [
'lookup' => ['character_id', 'realm_id', 'guild_id'],
'data' => [
'character_id' => $this->character->id(),
'realm_id' => $guild->realm()->id(),
'guild_id' => $guild->id(),
'guild_name' => $guild->name(),
]
];
}
return $this;
}
And this is how the entire CharacterEntity is generated
public function complete() : CharacterEntity {
$cacheKey = sprintf('services::api::blizzard::characters::%s:%s', $this->realm, $this->name); // Create Redis cache key
return Cache::remember($cacheKey, now()->addMinutes(5), function() { // Remeber character details in the cache for 5 minutes
$character = $this->profile(); // Get character profile information `profile/wow/character/kazzak/Astratheon`
try {
// Try to load character equipment details `profile/wow/character/kazzak/Astratheon/equipment`
$character->withEquipment($this->equipment());
} catch (\Exception $exception) {}
try {
// Try to load character media details `profile/wow/character/kazzak/Astratheon/character-media`
$character->withMedia($this->media());
} catch (\Exception $exception) {}
try {
// Try to load character progression details `profile/wow/character/kazzak/Astratheon/encounters/raids`
$character->withProgression($this->progression());
} catch (\Exception $exception) {}
try {
// Try to load character specializations details `profile/wow/character/kazzak/Astratheon/specializations`
$character->withSpecializations($this->specializations());
} catch (\Exception $exception) {}
return $character;
});
}
And this is an update logic in text
format
-
Query API to load data about all guild members
-
Get list of characters from database and assign respective models to the IDs of the guild members retrieved from API
$models = [
12345678 => null, // New character
57473472 => DatabaseModel, // Model of the character in local database
]
-
Iterate over the $models
array and fetch data for all characters
-
In case if for some reason the guild
for the member of the $models
array is null
, remove that character from database
a. This could happen if character has actually left the guild
b. Also, if it is not null, BUT does not equals to my guild name, character should be removed
c. Funny part: API returned 403 after the game build changed and character havent logged into the game
-
Finish the job - added new characters if there are any, removed all which do not have guild associated with them
P.S. If you need any further clarifications, i am here to help
P.P.S. Namespaces are fine as all of the code covered by the tests, and all of them do pass. But just in case, the namespace is profile-${region}
P.P.P.S. “Root of all problems” is - profile/wow/character/%realm%/%character_name%
, if it is missing data after the game build update, other endpoints might return 403. For example, if profile has no guild attached to the character (and i know character has guild as i’ve requested list of guild members before requesting profile information for a particular guild member), media endpoint will throw 403