Загружаем сразу много матчей используя dota2-api

В предыдущей заметке мы рассмотрели загрузку данных по одному матчу, зная его match_id. Чаще требуется загрузить сразу несколько матчей по какому-то критерию. Например, игры одного человека. Сразу разочарую тех, кто хочет загрузить все свои 3000+ игр. API дает возможность получить только последних 500. Обойти это ограничение не получится. Ладно, посмотрим, как получить хотя бы то, что дают. Для этого в dota2-api есть класс Dota2api\Mappers\MatchesMapperWeb:

require_once 'vendor/autoload.php';
require_once 'api-key.php';
 
\Dota2Api\Utils\Request::$apiKey = API_KEY;
$mapper = new Dota2Api\Mappers\MatchesMapperWeb();
$mapper->setAccountId(76482434); // Admiral Bulldog
 
/* $var Dota2Api\Models\Match[] */
$matches = $mapper->load();

Так мы получим первых 100 матчей из нужных 500. Как достать оставшиеся 400? К.О. говорит, что нужен цикл. Он прав. Но для цикла необходимо условия выхода из него — какой-то критерий, что все матчи уже загружены. API всегда возвращает в ответе поля total_results и results_remaining. Первое указывает, сколько всего записей можно получить по заданному критерию, а второе — сколько еще записей осталось. То есть, для запроса выше total_results будет 500, а results_remaining — 400. У MatchesMapperWeb есть поле start_at_match_id, с помощью которого можно задать «смещение» в результирующем наборе. В маппере это значение задается через метод setStartAtMatchId.

while ($mapper->getResultsRemaining() != 0) {
    $match = end($matches);
    if (!$match) {
        break;
    }
    $mapper->setStartAtMatchId($match->get('match_id') - 1);
    $newMatches = $mapper->load();
    if (!is_null($newMatches)) {
        $matches = array_merge($matches, $newMatches);
    }
}
echo count($matches)."\n"; // 500
echo $mapper->getResultsRemaining()."\n"; // 0
echo $mapper->getTotalMatches()."\n"; // 500

Что делать с полученными матчами — дело ваше. Но лучше сохранить их в БД, чтобы не дергать API лишний раз. Данные о матчах уже не изменятся — ведь эти матчи уже сыграны. К вопросу сохранения матчей в локальную базу данных мы еще вернемся. Пока обсудим ряд параметров, которые доступны для уточнения критериев поиска. На странице wiki-GetMatchHistory их перечислен добрый десяток. Но, к сожалению, большинство их них доступны только на «бумаге».

Начнем с параметра game_mode. Предположим, что нам интересны игры AdmiralBulldog’а только в CM:

$mapper = new Dota2Api\Mappers\MatchesMapperWeb();
$mapper->setAccountId(76482434); // Admiral Bulldog
$mapper->setGameMode(2); // CM
$matches = $mapper->load();

Результат выборки ровно такой-же, как и без указания CM. А может он и играл только CM? Для чистоты эксперимента зададим значение -1 (такого режима в игре нет):

$mapper = new Dota2Api\Mappers\MatchesMapperWeb();
$mapper->setAccountId(76482434); // Admiral Bulldog
$mapper->setGameMode(-1); // invalid value
$matches = $mapper->load();

И снова результат выборки точно такой же. Идем дальше. Параметр date_min указывает, что нас интересуют матчи только начиная с определенной даты. Хороший параметр, но тоже не работает. В качестве проверочного значения можно указать будущую дату и посмотреть, что вернет API:

$mapper = new Dota2Api\Mappers\MatchesMapperWeb();
$mapper->setAccountId(76482434); // Admiral Bulldog
$mapper->setDateMin(strtotime('+1 week')); // Back to the Future
$matches = $mapper->load();

В примере мы задаем дату на неделю позже текущей. Результат как и раньше — 500 матчей всего и 100 последних в текущем ответе.

Следующий параметр — leagueid. Он позволяет отфильтровать матчи, которые относятся к какому-то турниру. Возьмем к примеру The International 2016 (на момент публикации заметки прошли только отборы на него). Команда The Alliance (где играет AdmiralBulldog) провела на нем 18 матчей. Посмотрим, что вернет API, если задать leagueid равной 4664 (соответствует TI6):

$mapper = new Dota2Api\Mappers\MatchesMapperWeb();
$mapper->setAccountId(76482434); // Admiral Bulldog
$mapper->setLeagueId(4664); // The International 6
$matches = $mapper->load();

Данный параметр работает и в ответе приходит уже не 500 матчей, а «всего» 18. Однако значение total_results из API почему-то возвращается равным 12. С leagueid бывает еще другая «лажа», которая от нас никак не зависит. Известны факты, когда, при использовании данного фильтра, возвращается далеко не весь набор матчей. На момент публикации заметки, для TI6 возвращается где-то 190 матчей из почти 230 (тут используется фильтр только по leagueid — без account_id или еще чего-то). С чем это связано — нeпонятно. Будет ли исправлено — вопрос открытый.

По ссылке на wiki-GetMatchHistory есть еще несколько параметров, которые используются как фильтры, но все они так же работают с переменным успехом или не работают вообще. Очень жаль.

Поговорим о том, как сохранять матчи в локальную БД и о том, как их оттуда доставать (тоже пачкой).

Матчи в базу данных заносятся по одному:

$mapperDb = new Dota2Api\Mappers\MatchMapperDb();
foreach($matches as $match) {
    $mappedDb->save($match);
}

Довольно топорно, но «оно работает!». Больше о сохранении матчей сказать нечего 🙂 Переходим к их чтению из БД. В dota2-api для этого есть класс Dota2Api\Mappers\MatchesMapperDb. В нем есть целый ряд фильтров, которые реально фильтруют, а не просто существуют для галочки.

$mapper = new Dota2Api\Mappers\MatchesMapperDb();
$matches = $mapper->load();

Код, приведенный выше, вернет вообще все матчи, которые есть в БД. Просто и сердито. А если нам нужны матчи только одного игрока (в начале заметки мы пробовали такой же фильтр для API)?

$mapper = new Dota2Api\Mappers\MatchesMapperDb();
$mapper->setAccountId(76482434); // Admiral Bulldog
$matches = $mapper->load();

Известный факт, что AB много играл на Lone Druid’e. Фильтр по hero_id к вашим услугам:

$mapper = new Dota2Api\Mappers\MatchesMapperDb();
$mapper->setAccountId(76482434); // Admiral Bulldog
$mapper->setHeroId(80); // Lone Druid
$matches = $mapper->load();

Фильтры применяются через «AND», так что результатом будут все матчи AB на мишке.

Параметр leagueid тоже доступен как фильтр. Его применение абсолютно идентично свою собрату в MatchesMapperWeb:

$mapper = new Dota2Api\Mappers\MatchesMapperDb();
$mapper->setLeagueId(4664); // The International 6
$matches = $mapper->load();

Еще dota2-api дает возможность фильтровать матчи по идентификатору команды-участника. Для команды The Alliance он равен 111474 (где его взять разберем в других заметках).

$mapper = new Dota2Api\Mappers\MatchesMapperDb();
$mapper->setLeagueId(111474); // The Alliance
$matches = $mapper->load();

Данный фильтр проверяет как radiant, так и dire стороны.

Как уже можно было догадаться, MatchesMapperDb поддерживает комбинации фильтров. Например, фильтр «все матчи AdmiralBulldog на TI6 на медведе» будет выглядеть так:

$mapper = new Dota2Api\Mappers\MatchesMapperDb();
$mapper->setLeagueId(4664);      // The International 6
$mapper->setAccountId(76482434); // Admiral Bulldog
$mapper->setHeroId(80);          // Lone Druid
$matches = $mapper->load();

Если на момент выполнения этого скрипта в вашей локальной БД будут все матчи с TI6 (227 отборочных на момент публикации заметки), то как результат вы получите 2 матча — против PR (2463703523) и Ad Finem (2467484798).

Записать матчей в БД, их чтение оттуда мы уже обсудили. Обновление матчей в БД смысла не имеет, так как по завершению игры, ее показатели уже не меняются. Из классического CRUD осталась только буква D — delete. Удаление матчей очень простое. Есть метод delete, который принимает массив идентификаторов матчей и удаляет из БД все, что с ними связано — общие данные про матч, данные про игроков, пикобаны и т.д:

$mapper = new Dota2Api\Mappers\MatchesMapperDb();
$mapper->delete([2463703523, 2467484798]); // two matches from previous example

Вот и все, что касается простого CRUD для матчей. В качестве еще одного небольшого совета — не надейтесь загрузить из API нужный вам блок матчей за один раз. Например, сейчас из API уже не получить все матчи отборов TI6. Как стоило бы поступить — пока шли отборы, раз в два часа выгружать из API все доступные матчи и новые загружать в БД. В dota2statistic.com мы вообще раз в 10 минут (через Cron) выполняли скрипт вида:

$mapperWeb = new Dota2Api\Mappers\MatchesMapperWeb();
$mapperDb = new Dota2Api\Mappers\MatchMapperDb();
$ti6 = 4664;
$mapperWeb->setLeagueId($ti6);
$mapperDb->setLeagueId($ti6);
$matchesFromApi = $mapperWeb->load();
while ($mapperWeb->getResultsRemaining() != 0 && count($matchesFromApi) > 0) {
    $lastMatchId = -1;
    foreach($matchesFromApi as $match) {
        $mapperDb->save($match);
        $lastMatchId = $match->get('match_id');
    }
    $mapperWeb->setStartAtMatchId($lastMatchId - 1);
    $matchesFromApi = $mapperWeb->load();
}

, ,

Сборник рецептов по dota2-api 

Оставить комментарий

Top ↑ | Main page | Back