Yii PHP framework: создаём игровой сайт. Часть 5. Импорт игр.

Сегодня мы продолжим создание игрового сайта с помощью Yii PHP framework. Наши задачи: заполнить базу данных информацией об играх (её мы берем из партнерки GameBoss) и, заодно, немного разобраться с библиотекой Yii для работы с базой данных.
Алгоритм импорта довольно прост.
1) Читаем из базы список жанров. Они должны быть созданы заранее, иначе мы не сможем правильно создать записи в таблице ygs_games_types.
2) Получаем список игр, которые уже сохранены в БД (нам нужны их id).
3) Получаем данные от GameBoss. Они приходят в xml формате.
4) Для каждой игры, полученной от GameBoss, проверяем, существует ли она в базе, и, если нет, сохраняем.
5) Создаём записи в связанных таблицах ygs_games_types и ygs_screenshots.
6) Показываем страницу с кнопкой «Импрорт».
Прежде чем писать метод импорта, создадим модель, контроллер и представления для работы с играми.
>> yiic shell
model Games ygs_games
crud Games
Теперь можно добавить метод actionImport в контроллер GamesController.
-
public function actionImport() {
-
//получаем список всех жанров
-
$types = Types::model()->findAll();
-
//ищем все сохраненные игры (их id)
-
$existingIds = Games::model()->getExistingIds();
-
$errors = array();
-
$results = ";
-
//обработка команды
-
if (isset($_POST['import'])) {
-
libxml_use_internal_errors(true);
-
//загружаем xml фид
-
$xml = simplexml_load_file(Yii::app()->user->xml);
-
if (!$xml) {
-
$errors = libxml_get_errors();
-
}
-
else {
-
$i = 0;
-
//парсинг фида
-
foreach ($xml->result->ITEM as $game) {
-
//если эта игра уже сохранена…
-
if (in_array($game->ID, $existingIds)) {
-
//…переходим к следующей
-
continue;
-
}
-
//создаем новую игру
-
$newGame = new Games;
-
//заполняем атрибуты
-
$newGame->g_id = $game->ID;
-
$newGame->g_rate = $game->RATE;
-
$newGame->g_name_url = $game->NAME_URL;
-
$newGame->g_type = $game->TYPE;
-
$newGame->g_added = $game->ADDED;
-
$newGame->g_size = $game->SIZE;
-
$newGame->g_name = $game->NAME;
-
$newGame->g_medium_pic = $game->MEDIUM_PIC;
-
$newGame->g_small_pic = $game->SMALL_PIC;
-
$newGame->g_download_link = $game->DOWNLOAD_LINK;
-
$newGame->g_shortdescr = $game->SHORTDESCR;
-
$newGame->g_fulldescr = $game->FULLDESCR;
-
$newGame->g_publish_date = date('Y-m-d', time());
-
$newGame->g_state = Games::PUBLISHED;
-
//записываем массив со скриншотами
-
foreach ($game->SCREENSHOT as $sh) {
-
$newGame->g_screenshots[] = $sh;
-
}
-
//разбираем поле с жанрами
-
foreach ($types as $type) {
-
if ($newGame->g_type & $type['t_id']) {
-
$newGame->g_types[] = $type['t_id'];
-
}
-
}
-
//сохраняем игру в БД
-
//сохранение скриншотов и жанров выполняетя в Games::afterSave()
-
if (!$newGame->save()) {
-
$errors[] = 'Не могу сохранить игру id = '.$newGame->g_id;
-
}
-
else {
-
$i++;
-
}
-
}
-
$results = 'Сохранено новых игр '.$i;
-
}
-
}
-
//показываем форму
-
$this->render('import',
-
array('xml'=>Yii::app()->user->xml, 'errors'=>$errors, 'results'=>$results));
-
}
Как видите, он полностью соответствует описанному алгоритму.
С помощью Types::model()->findAll() мы получаем массив со всеми жанрами игр.
Далее получаем массив всех уже записанных в БД игр (с помощью Games::model()->getExistingIds()). Обратите внимание, что id игр мы берем из фида партнерки, т.е., используя этот id, мы всегда можем сравнить игру в базе и игру в фиде. Мы не можем указать какие именно игры нужно получить из базы партнерки, можно указать только их количество. Поэтому мы запрашиваем фид со всеми играми, и если в нём появились новые, сохраняем их в базе.
Для парсинга фида используем библиотеку SimpleXML (на мой взгляд, она одна из самых удобных для работы с XML).
Фид можно получить, используя примерно такой запрос
-
http://gameboss.ru/x2.php?partner=38370&limit=1000&genre=127&short=1&full=1&image=1
-
Данные выглядят следующим образом.

В каждую отдельную игру (
item) входят следующие данные.
Теперь взгляните на строки 26-41 и сравните их с последним скриншотом. Как видите, мы получаем данные игры, используя имена тегов.
Для каждой новой игры мы создаём новый объект типа
Game(строка 26) (классGamesнаходится в папкеprotected\models\Games.php– это наша модель).Модель автоматически предоставляет нам доступ ко всем полям в таблице
ygs_games, но нам нужно сохранить данные о типах игры и скриншотах.Поэтому добавим в модель несколько свойств.
-
class Games extends CActiveRecord {
-
…
-
public $g_screenshots = array();
-
public $g_types = array();
-
//это свойство используется для того, чтобы отключить удаление
-
//скриншотов при обновлении страницы, т.к. форма обновления не
-
//содержит списка скриншотов
-
public $updateScreenshots = true;
-
-
const PUBLISHED = 0;
-
const DRAFT = 1;
-
}
$g_screenshotsи$g_typesиспользуются для хранения скриншотов и жанров, которые относятся к данной игре.Вернемся к методу
actionImport. Мы заполняем массивы$g_screenshotsи$g_typesсоответствующими данными (строки 43-51), и сохраняем игру (строка 54).Для того, чтобы получить жанры игр, мы используем побитную операцию сложения. Жанры имеют коды: 1, 2, 4, 8, 16 и т.д., т.е. двойка в соответствующей степени. Поэтому если код жанра 3 (0000.0011), то это значит, что игра относится к жанрам 1 (0000.0001) и 2 (0000.0010).
Библиотека Yii сама запишет данные в таблицу
ygs_games, т.к. модель Games создана по этой таблице. Но сохранить скриншоты и жанры игр она сама не сможет. Поэтому мы должны сделать это вручную.Чтобы не писать код везде, где используется метод
save(), мы можем добавить в модель методafterSave, который будет вызываться автоматически каждый раз, когда мы используемsave().-
public function afterSave() {
-
//если запись уже существует, то перед обновлением удаляем все связанные данные
-
if (!$this->isNewRecord) {
-
$this->dbConnection->createCommand('DELETE FROM ygs_games_types WHERE gt_game_id='.$this->g_id)->execute();
-
//скриншоты удаляем только если updateScreenshots == true
-
if ($this->updateScreenshots) {
-
$this->dbConnection->createCommand('DELETE FROM ygs_screenshots WHERE s_game_id='.$this->g_id)->execute();
-
}
-
}
-
//сохраняем жанры
-
foreach ($this->g_types as $type) {
-
if (($t = Types::model()->findByPk($type)) !== null) {
-
$this->dbConnection->createCommand('INSERT INTO ygs_games_types (gt_game_id, gt_type_id) VALUES ('.$this->g_id.','.$type.')')->execute();
-
}
-
}
-
//сохраняем скриншоты
-
if ($this->updateScreenshots) {
-
$command = $this->dbConnection->createCommand('INSERT INTO ygs_screenshots (s_game_id, s_image, s_thumbnail) VALUES (:s_game_id, :s_image, :s_thumbnail)');
-
foreach ($this->g_screenshots as $screenshot) {
-
$command->bindParam(':s_game_id', $this->g_id, PDO::PARAM_INT);
-
$command->bindParam(':s_image', $screenshot->IMAGE, PDO::PARAM_STR);
-
$command->bindParam(':s_thumbnail', $screenshot->THUMBNAIL, PDO::PARAM_STR);
-
$command->execute();
-
}
-
}
-
}
Тут нужны некоторые пояснения. Прежде всего, взгляните на использование свойства
isNewRecord(строка 3). Библиотека Yii сама его изменяет, и оно позволяет определить, работаем ли мы с новой игрой или она была ранее сохранена в БД.В данном случае, если игра была сохранена ранее, мы удаляем все связанные с ней жанры и скриншоты, т.е. все записи из таблиц
ygs_games_typesиygs_screenshotsу которыхgame_idравенidданной игры (строки 3-9).Затем мы сохраняем скриншоты и жанры из массивов
g_screenshotsиg_typesв базе (строки 11-25).Вы, наверное, заметили, что здесь мы работаем с базой напрямую, т.е. сами создаём SQL запросы. Фреймворк предоставляет такую возможность, т.к. не во всех случаях удобно использовать его AR библиотеку.
Получить текущее соединение можно с помощью свойства
$this->dbConnection, а для выполнения запроса используются методыexecute()иquery()(первый для отправки запросов на запись/изменение/удаление, а второй – для чтения).Для формирования запроса используется метод
createCommand. Взгляните на строки 18-22. Если вы раньше работали с PDO, то сразу поймете принцип. Библиотека Yii работает на основе PDO, поэтому нет ничего удивительного, что установка параметров запроса выполняется точно также.В запросе можно указать имена параметров, используя двоеточие, а потом с помощью метода
bindParamустанавливать значения. При этом все символы автоматически экранируются.Представление, которое формирует метод
actionImport, содержит форму с одной кнопкой «Импортировать». После импорта мы показываем количество сохранённых игр.Код представления я здесь приводить не буду, вы всегда можете посмотреть его в архиве с исходниками сайта.
Как видите, импортировать игры не сложно. Главное – правильно записать данные в базу.
Если у вас есть вопросы или замечания, пишите. В следующий раз мы займемся созданием страниц с перечнем игр.
Все разделы цикла.
- Yii PHP framework: создаём игровой сайт. Часть 1. Постановка задачи.
- Yii PHP framework: создаём игровой сайт. Часть 2. База данных и установка фреймворка.
- Yii PHP framework: создаём игровой сайт. Часть 3. Аутентификация.
- Yii PHP framework: создаём игровой сайт. Часть 4. Работа с жанрами игр.
- Yii PHP framework: создаём игровой сайт. Часть 5. Импорт игр.
- Yii PHP framework: создаём игровой сайт. Часть 6. Формируем страницы игр и жанров.
- Yii PHP framework: создаём игровой сайт. Часть 7. Работа с JavaScript и страницы игр.
- Yii PHP framework: создаём игровой сайт. Часть 8. Создаём виджеты.
- Yii PHP framework: создаём игровой сайт. Часть 9. Поиск ошибок.
- Yii PHP framework: создаём игровой сайт. Часть 10. Панель управления.
- Yii PHP framework: создаём игровой сайт. Часть 11. Человекопонятные URL.
- Архив с исходниками
Интересно почитать
Хороший блог о веб-дизайне расскажет как пользоваться фотошопом.
-
Понравилась статья? Подписывайтесь на продолжение
!
Опубликовано в PHP, Web разработка, Yii Комментарии (7) »
Комментарии (7)
Вы можете отслеживать обсуждение записи с помощью RSS 2.0 ![]()
Вы также можете оставить комментарий, или трекбек с Вашего сайта.










Прочитал и понял почему в свое время не выбрал этот фреймворк. Слишком сложно…
А что именно сложно? Работа с БД?
Просто сложно))) Я привык к Codeigniter, который считаю самым простым в изучении.
А мне кажется все достаточно понятным )
Знаете, при каких обстоятельствах изучение библиотеки становится сложным?
ИМХО, когда нет никакой документации и приходится лезть в код (особенно если он еще не документирован), и методом "тыка" выискивать нужный функционал, один за другим перебирая классы, которых целые мегабайты. А если это dll-ка, которую можно познать только путем дизассемблирования… то можно сразу вешаться =)
Если попробовать пару раз разобраться в чужом коде не имея документации, то любые пояснения, даже на зарубежных языках (а тем более на русском), становятся глотком свежего воздуха в познании и понимании функционала.
Для меня это тоже сложновато было понять, но все равно спасибо!
Как по мне, так лучше Codeignite еще ничего непридумано!
Хороший у вас сайт!