1. Нужна дополнительная (к User) таблица.
Код: Выделить всё
<?php
use yii\db\Migration;
class m180608_123330_create_social_link extends Migration
{
public $table = '{{%social_link}}';
public function up()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
}
$this->createTable($this->table, [
'id' => $this->primaryKey(),
'user_id' => $this->integer()->notNull(),
'source' => $this->string(255)->notNull(),
'source_id' => $this->string(255)->notNull(),
], $tableOptions);
$this->addForeignKey ('FK_social_link_user',
$this->table, 'user_id', '{{%user}}', 'id', 'CASCADE');
}
public function down()
{
$this->dropTable($this->table);
}
}
Код: Выделить всё
<?php
namespace common\models;
use yii\db\ActiveRecord;
use common\models\User;
class SocialLink extends ActiveRecord
{
public static function tableName()
{
return '{{%social_link}}';
}
public function rules()
{
return [
[['user_id', 'source', 'source_id'], 'required'],
['user_id', 'integer'],
[['source', 'source_id'], 'string', 'max'=>255],
];
}
public function getUser()
{
return User::findOne($this->user_id);
}
}
Но формат данных разный, поэтому нужно сначала привести значения к общему виду (в конструкторе) и потом регистрировать контакт.
Код: Выделить всё
namespace common\components;
use Yii;
use yii\base\BaseObject;
use yii\base\InvalidValueException;
use common\models\SocialLink;
use common\models\User;
class SocialContact extends BaseObject
{
public $id;
public $name;
public $email;
private $_link;
/**
* Retrieve id, name, email and, may be more.
*
* @param obj social client (OAuth2 response)
* @param array config
*/
public function __construct($client, $config = [])
{
$client_id = $client->getId();
$attributes = $client->getUserAttributes();
$this->id = (string)$attributes['id'];
// convert from individual to a single view of attributes
$class_name = 'common\\components\\convertor\\' .
ucfirst($client_id);
$convertor = new $class_name();
$convertor->set($this, $attributes);
// if email not setted then make it
if (!$this->email)
$this->email = "{$attributes['id']}@{$client_id}.net";
parent::__construct($config);
}
public function registration($client_id)
{
if (User::find()->where(['email' => $this->email])->exists()) {
Yii::$app->getSession()->setFlash('error', [
Yii::t('app',
'User with {email} have been exist, but not linked to {client}. ' .
'Try to login with other social network or with name and password.', [
'email' => $this->email,
'client' => $client_id,
]),
]);
} else {
if(User::find()->where(['name' => $this->name])->exists()) {
Yii::$app->getSession()->setFlash('error', [
Yii::t('app',
'A user named {name} already exists. ' .
'Try logging in if you have registered before.', [
'name' => $this->name,
]),
]);
} else {
$password = Yii::$app->security->generateRandomString(6);
$user = new User([
'name' => $this->name,
'email' => $this->email,
'password' => $password,
'status' => User::STATUS_ACTIVE,
]);
$user->generateAuthKey();
$user->generatePasswordResetToken();
$transaction = $user->getDb()->beginTransaction();
if ($user->save()) {
if ($this->makeLink($client_id, $user->id)) {
$transaction->commit();
Yii::$app->user->login($user);
} else {
throw new InvalidValueException($this->showErrors($this->_link));
}
} else {
throw new InvalidValueException($this->showErrors($user));
}
}
}
}
public function makeLink($client_id, $user_id)
{
$this->_link = new SocialLink([
'user_id' => $user_id,
'source' => $client_id,
'source_id' => $this->id,
]);
return $this->_link->save();
}
/**
* Show $model errors in dev mode
* @param object model
* @return string errors message
*/
private function showErrors($model)
{
$out = 'Can\'t save ' . $model->tableName() . "\n";
foreach($model->getErrors() as $field => $messages) {
$out .= "«{$field}»\n";
foreach($messages as $message) {
$out .= "{$message}\n";
}
$out .= "\n";
}
return $out;
}
}
Код: Выделить всё
namespace common\components\convertor;
class Yandex implements Convertor {
public function set($obj, $attributes)
{
$obj->name = $attributes['first_name'] . ' ' . $attributes['last_name'];
$obj->email = isset($attributes['default_email'])
? $attributes['default_email']
: false;
}
}
Код: Выделить всё
public function onAuthSuccess($client)
{
$social_contact = new SocialContact($client);
$social_link = SocialLink::find()->where([
'source' => $client->getId(),
'source_id' => $social_contact->id,
])->one();
if (Yii::$app->user->isGuest) {
if ($social_link) { // authorization
Yii::$app->user->login($social_link->user);
} else { // registration
$social_contact->registration($client->getId());
}
} else { // the user is already registered
if (!$social_link) { // add external service of authentification
$social_contact->makeLink($client->getId(), Yii::$app->user->id);
}
}
}
Полное описание подключения расширения yiisoft/yii2-authclient - http://vorst.ru/post/authorization-social-network.
Расширение, включающее авторизацию через социальные сети - https://github.com/sergmoro1/yii2-user.