В предыдущей статье мы разобрались, что из себя представляет VK API, какие он дает возможности и написали простенькое приложение, которое авторизует пользователя и получает информацию о нем. В этой статье мы будем добавлять группы, стену которых в будущем будем парсить и получим основную информацию о паблике.
Рекомендую вам сразу выкачать код с репозитория. Так как изменения довольно объёмные, а в этой статье я буду описывать только основные нюансы. Ссылка на репозиторий.
Я поменял структуру приложения. Первое, что сделал, это добавил psr-4
загрузчик, Он позволит нам красиво оформить неймспейсы, четко разделяя приложение на компоненты. Для этого нужно привести файл composer.json
к виду:
{ "require": { "slim/slim": "^3.0", "twbs/bootstrap": "4.0.0-alpha.6", "illuminate/database": "^5.4", "slim/twig-view": "^2.2", "guzzlehttp/guzzle": "~6.0" }, "autoload": { "psr-4": { "App\\": "app/" } } }
Переделал файл с маршрутами. Теперь маршруты выполняют методы указанных контроллеров:
<?php $app->get('/', \App\Controllers\SiteController::class .':index'); $app->get('/authorize', \App\Controllers\SiteController::class . ':authorize'); $app->get('/groups', \App\Controllers\GroupsController::class .':index'); $app->post('/groups/add', \App\Controllers\GroupsController::class.':insert');
Каждый контроллер, должен в конструкторе принимать объект контейнера. В контейнере находится вся информация о нашем приложении и для того, чтобы класс мог работать с приложением, нужно в конструктор класса передать этот объект.
Раньше для авторизации пользователя мы выполняли методы в колбеках наших маршрутов, сейчас я вынес все в отдельный класс:
SiteController.php
<?php namespace App\Controllers; use \Interop\Container\ContainerInterface as ContainerInterface; use App\Controllers\Controller; use App\Classes\VK; class SiteController extends Controller { private $vk; public function __construct (ContainerInterface $container) { $this->container = $container; $this->vk = new VK($this->container->get('settings')['vk']); } public function index ($request, $response, $args) { if (isset($_SESSION['vk'])) { if (!isset($_SESSION['account'])) { // Устанавливаем токен $this->vk->accessToken = $_SESSION['vk']->access_token; // Получаем информацию о текущем пользователе // Записываем всю информацию в сессию $_SESSION['account'] = $this->vk->getAccountInfo($_SESSION['vk']->user_id); } return $this->container->view->render($response,'index.html', ['vk' => $this->vk, 'account' => $_SESSION['account']->response]); } return $this->container->view->render($response,'index.html', ['vk' => $this->vk]); } public function authorize ($request, $response, $args) { if ($request->getQueryParam('code') != NULL) { // Получаем токен $_SESSION['vk'] = $this->vk->getAccessToken($request->getQueryParam('code')); } return $response->withStatus(302)->withHeader('Location', '../'); } }
Все предельно понятно, тоже самое, что и было в роутах, только теперь в отдельном классе.
Следующий класс у нас, это GroupsController
. Класс который пока еще обладает функционалом отображения списка групп и добавлением их в базу.
GroupsController.php
<?php namespace App\Controllers; use \Interop\Container\ContainerInterface as ContainerInterface; use App\Controllers\Controller; use App\Classes\VK; use App\Models\Group; class GroupsController extends Controller { public function __construct (ContainerInterface $container) { $this->container = $container; } public function index ($request, $response, $args) { $account = $_SESSION['account']->response; $groups = Group::orderBy('created_at', 'desc')->get(); return $this->container->view->render($response, 'groups.html', ['account' => $account, 'groups' => $groups]); } public function insert ($request, $response, $args) { $data = $request->getParsedBody(); if (!isset($data['id'])) { return $response->withJson(['success' => false, 'msg' => 'Не указан ID группы.']); } if ($data['id'] == '') { return $response->withJson(['success' => false, 'msg' => 'ID не должен быть пустым.']); } if (!is_int( (int) $data['id'])) { return $response->withJson(['success' => false, 'msg' => 'ID содержит недопустимые символы.']); } $groupExists = Group::where('group_id', (int) $data['id'])->count(); if ($groupExists > 0) { return $response->withJson(['success' => false, 'msg' => 'Группа с таким ID уже добавлена.']); } $group = new Group; $group->group_id = (int) $data['id']; if ($group->save()) { $vk = new VK($this->container->get('settings')['vk']); $vk->accessToken = $_SESSION['vk']->access_token;; $groupInfo = $vk->getGroupInfo($group->group_id); $data = $groupInfo[0]; $group->name = $data->name; $group->type = $data->type; $group->avatar = $data->photo_50; $group->followers = $data->members_count; $group->update(); return $response->withJson(['success' => true, 'msg' => 'Группа успешно добавлена.', 'data' => $group]); } return $response->withJson(['success' => false, 'msg' => 'Ошибка.']); } }
Здесь уже посложнее. Первое, что бросается в глаза, это участок кода в самом начале:
use App\Classes\VK; use App\Models\Group;
Этим мы подключаем к нашему классу модель для работы с группами и класс VK
, который предоставляет нам методы для работы с API, модель в свою очередь позволит нам работать с базой данных, но об этом ниже.
Метод index
служит для отображения страницы с группами и вывода на неё информации. Другой метод под именем insert
служит для добавления групп в базу. Валидации данных не очень красивые, но на первое время подойдут, в будущем напишем свой класс валидации данных. Как вы могли заметить, все ответы, что будут отправлены клиенту, я отдаю в формате json
. Это связано с тем, что добавляю я группы при помощи AJAX
, поэтому ответ должен быть в формате json
. Я писал небольшой туториал по работе с AJAX на примере загрузки фотографии на сервер.
С добавлением в базу группы все просто, сначала мы записываем в базу ID группы, который получили из формы.
$group = new Group; $group->group_id = (int) $data['id'];
Далее, если данные сохранились успешно, мо делаем запрос к API вконтакте, получаем данные и задаем атрибуты нашего объекта группы, обновляем модель.
if ($group->save()) { $vk = new VK($this->container->get('settings')['vk']); $vk->accessToken = $_SESSION['vk']->access_token;; $groupInfo = $vk->getGroupInfo($group->group_id); $data = $groupInfo[0]; $group->name = $data->name; $group->type = $data->type; $group->avatar = $data->photo_50; $group->followers = $data->members_count; $group->update(); return $response->withJson(['success' => true, 'msg' => 'Группа успешно добавлена.', 'data' => $group]); }
Метод getGroupInfo
, делает запрос к API и возвращает нам данные о конкретной группе.
public function getGroupInfo ($id) { $url = 'https://api.vk.com/method/groups.getById'; $client = new \GuzzleHttp\Client(); $response = $client->request('POST', $url, [ 'form_params' => [ 'group_id' => $id, 'fields' => 'photo_50,members_count', 'access_token' => $this->accessToken ], 'verify' => false ]); $data = json_decode($response->getBody()); return $data->response; }
Для работы с базой я взял ORM Eloquent
, кто имел дело с фреймворком Laravel
, тот знает, что это такое. Тем кто дела не имел, в кратце пару слов. ORM — это своеобразная прослойка между программистом и базой данных. Используя ту или инную ORM вам больше не нужно писать sql запросы к базе данных, нужно лишь настроить подключение и использовать методы, которые предоставляет ORM. Также ORM даёт нам возможность работать с данными в базе как с объектом. Простой пример:
Вместо обычного:
$groups = $pdo->query('SELECT * FROM groups ORDER BY created_at DESC');
Мы напишем:
$groups = Group::orderBy('created_at', 'desc')->get();
На выходе во втором случае мы получим коллекцию объектов, с которыми будет очень удобно работать. Более подробно о методах данной ORM написано в официальной документации.
Файл модели выглядит так:
Group.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Group extends Model { protected $table = 'groups'; }
Запрос на создание таблицы groups
в базе данных:
CREATE TABLE IF NOT EXISTS `groups` ( `id` int(11) NOT NULL AUTO_INCREMENT, `group_id` int(10) UNSIGNED DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `avatar` varchar(255) DEFAULT NULL, `type` varchar(255) DEFAULT NULL, `followers` int(10) UNSIGNED DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `group_id` (`group_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Файл для подключения к базе данных:
database.php
<?php use Illuminate\Database\Capsule\Manager as Capsule; $capsule = new Capsule; $capsule->addConnection([ 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'vkapi', 'username' => 'vkapi', 'password' => 'password', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', ]); $capsule->setAsGlobal(); $capsule->bootEloquent();
Для того, чтобы база данных была доступна в нашем приложении, добавим строчку в файл app.php
:
include 'database.php';
Страница для отображения списка групп и добавления новых имеет в себе таблицу, которая наполняется информацией о группах и небольшую форму для добавления новых групп. Для добавления группы, нужно ввести в форму её ID
.
{% extends "layout/app.html" %} {% block content %} <div class="row card" style="margin-top: 30px"> <div class="card-block"> {% if account %} <h1>Группы</h1> <hr> <button class="btn btn-primary" id="showForm">Добавить группу</button> <form class="form-inline" style="margin-top: 30px; display: none;" id="addNewGroup"> <div class="input-group mb-2 mr-sm-2 mb-sm-0"> <input type="text" name="id" placeholder="Ссылка на группу" class="form-control"> </div> <button type="submit" class="btn btn-success">Добавить</button> </form> <hr> <table class="table table-striped" id="groupsTable"> <thead> <tr> <th>#</th> <th>Название</th> <th>Количество подписчиков</th> <th>Добавлена</th> <th>Действия</th> </tr> </thead> <tbody> {% if groups|length > 0 %} {% for group in groups %} <tr> <td>{{ group.id }}</td> <td>{{ group.name }}</td> <td>{{ group.followers }}</td> <td>{{ group.created_at }}</td> <td>Действия</td> </tr> {% endfor %} {% endif %} </tbody> </table> {% else %} <h1>Авторизировать пользователя</h1> <hr> <a href="{{ vk.getLoginLink }}">Авторизация</a> {% endif %} </div> </div> {% endblock %} {% block scripts %} <script> $(document).ready(function () { $("#showForm").click(function () { $('#addNewGroup').slideToggle(); }); $('#addNewGroup').submit(function (f) { f.preventDefault(); var form = new FormData(this); $.ajax({ type:'POST', url: '{{ base_url() }}/groups/add', data: form, cache: false, contentType: false, processData: false, success:function(data){ var response = data.data; $('#groupsTable tbody').prepend('<tr><td>'+response.id+'</td><td>'+response.name+'</td><td>'+response.followers+'</td><td>'+response.created_at+'</td><td>Действия</td></tr>'); console.log(response); } }); }); }); </script> {% endblock %}
Теперь перейдя по адресу http://localhost/groups
(адрес может иметь другой вид, в зависимости от настройки вашего веб сервера), мы попадем на страницу с группами и сможем добавлять в список группы.
В следующей статье, будем удалять группы из списка и научимся парсить стену паблика.
Надеюсь у меня удалось раскрыть основные моменты, если что-то не понятно, пишите комментарии, поправим)