Сегодня мы продолжим создание игрового сайта с помощью 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
, содержит форму с одной кнопкой «Импортировать». После импорта мы показываем количество сохранённых игр.
Код представления я здесь приводить не буду, вы всегда можете посмотреть его в архиве с исходниками сайта.
SourceКак видите, импортировать игры не сложно. Главное — правильно записать данные в базу.
Если у вас есть вопросы или замечания, пишите. В следующий раз мы займемся созданием страниц с перечнем игр.
Все разделы цикла.
- 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.
- Архив с исходниками
Интересно почитать
Хороший блог о веб-дизайне расскажет как пользоваться фотошопом.