Ну очень простой чат на Yii2 + pjax

в 12:51, , рубрики: ajax, pjax, yii, yii2

Здравствуйте, уважаемые Хабровчане! Решил поделиться с вами довольно простым криворуким приёмом, как за 10 минут "поднять" чатик на "голом" yii2 с помощью pjax. Кому интересно про что речь, добро пожаловать под кат.

Думаю что большинство читателей, знакомых с yii2, также знакомы и с технологией pjax.

БД


От БД нам нужна всего 1 таблица с сообщениями, создадим её в свойственной yii манере, с помощью миграции:

$ ./yii migrate/create init

app/migrations/m160923_115323_init.php

use yiidbMigration;

class m160923_115323_init extends Migration
{
    public function up()
    {
        $this->createTable('message', [
            'id' => $this->primaryKey(),
            'from' => $this->integer()->notNull(),
            'to' => $this->integer()->notNull(),
            'text' => $this->text()->notNull()
        ]);
    }

    public function down()
    {
        $this->dropTable('message');
    }
}

На данном этапе можно сразу описать модель сообщения с довольно важным методом, с помощью которого мы в дальнейшем будет получать экземпляр ActiveQuery для формирования самого чата:

app/models/Message.php

namespace appmodels;

use yiidbActiveQuery;
use yiidbActiveRecord;
/**
 * @property int $id
 * @property int $from
 * @property int $to
 * @property string $text
*/
class Message extends ActiveRecord
{
    public function rules()
    {
        return [
            [['from', 'to', 'text'], 'required'],
            [['from', 'to'], 'integer'],
            ['text', 'string']
        ];
    }

    /**
     * @param int $from
     * @param int $to
     * @return ActiveQuery
     */
    public static function findMessages($from, $to)
    {
        return self::find()
            ->where(['from' => $from])
            ->orWhere(['from' => $to, 'to' => $from]);
    }
}

вот собственно и всё, что нам потребуется от БД.

Контроллер


Контроллер у нас будет состоять всего из одного "экшна":

app/controllers/ChatController.php

namespace appcontrollers;

use appmodelsMessage;
use Yii;
use yiifiltersAccessControl;
use yiihelpersVarDumper;
use yiiwebController;

class ChatController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                    ]
                ],
            ]
        ];
    }

    public function actionIndex($id)
    {
        $currentUserId = Yii::$app->user->identity->getId();
        $messagesQuery = Message::findMessages($currentUserId, $id);
        $message = new Message([
            'from' => $currentUserId,
            'to' => $id
        ]);
        if ($message->load(Yii::$app->request->post()) && $message->validate()) {
            $message->save();
            $message = new Message([
                'from' => $currentUserId
            ]);
            if (Yii::$app->request->isPjax) {
                return $this->renderAjax('_chat', compact('messagesQuery', 'message'));
            }
        }
        if (Yii::$app->request->isPjax) {
            return $this->renderAjax('_list', compact('messagesQuery', 'message'));
        }

        return $this->render('chat', compact('messagesQuery', 'message'));
    }
}

Постараюсь пояснить, что происходит в "экшне":

  1. При переходе на страницу загружается полностью контент вместе с layout
  2. После загрузки страницы запускается setInterval, который обновляет список сообщений
  3. При отправке сообщения в чат обновляется список совместно с формой

Вью


А вот тут, по моему мнению, самое интересное. Для начала постараюсь пояснить логику обновления "чатика" при помощи pjax:

image

В нашем случае нам нужно:

  1. Обернуть список с формой в pjax-контейнер для обновления всего списка сообщений и формы в случае отправки сообщения собеседнику;
  2. Обернуть только список в pjax-контейнер для периодического обновления списка сообщений.

app/views/chat/chat.php

<?php
/**
 * @var yiiwebView $this
 * @var appmodelsMessage $message
 * @var yiidbActiveQuery $messagesQuery
 */
?>
<?php yiiwidgetsPjax::begin([
    'timeout' => 3000,
    'enablePushState' => false,
    'linkSelector' => false,
    'formSelector' => '.pjax-form'
]) ?>
<?= $this->render('_chat', compact('messagesQuery', 'message')) ?>
<?php yiiwidgetsPjax::end() ?>
<?php $this->registerJs(<<<JS
function updateList() {
  $.pjax.reload({container: '#list-messages'});
}
setInterval(updateList, 1000);
JS
) ?>

app/views/chat/_chat.php

<?php
/**
 * @var yiiwebView $this
 * @var appmodelsMessage $message
 * @var yiidbActiveQuery $messagesQuery
 */
?>
<?php yiiwidgetsPjax::begin([
    'id' => 'list-messages',
    'enablePushState' => false,
    'formSelector' => false,
    'linkSelector' => false
]) ?>
<?= $this->render('_list', compact('messagesQuery')) ?>
<?php yiiwidgetsPjax::end() ?>
<?php yiiwidgetsActiveForm::begin(['options' => ['class' => 'pjax-form']]) ?>
<?= yiibootstrapHtml::activeTextarea($message, 'text') ?>
<?= yiihelpersHtml::submitButton('Отправить') ?>
<?php yiiwidgetsActiveForm::end() ?>

app/views/chat/_list.php

<?php
/**
 * @var yiiwebView $this
 * @var yiidbActiveQuery $messagesQuery
 */
?>
<?= yiiwidgetsListView::widget([
    'itemView' => '_row',
    'layout' => '{items}',
    'dataProvider' => new yiidataActiveDataProvider([
        'query' => $messagesQuery,
        'pagination' => false
    ])
]) ?>

app/views/chat/_row.php

<?php
/**
 * @var yiiwebView $this
 * @var appmodelsMessage $model
 */
?>
<div class="row">
    <div class="col-md-3"><?= $model->from ?></div>
    <div class="col-md-9"><?= $model->text ?></div>
</div>

Результат

Ну очень простой чат на Yii2 + pjax - 2

Автор: sluchainiyznak

Источник

  1. Андрей:

    Добрый день я всё зделал по инструкции,но выдаёт 404,
    может нужно дополнительно устанавливать какието расширения?

  2. Денис:

    Я бы тебя на работу не взял) Даже если учесть преимущество, что для чата на интервале не нужен демон. Зачем pjax + list ? Вызываешь метод обновления окна и параллельно очищаешь форму ввода при отправке, а по интервалу только вызываешь метод обновления окна… Хотя это все кастыли… Юзай вебсокеты!

    • Константин:

      А ты скинь пост, где показывается как его юзать, твой вебсокет. Я в интернете пошарил, везде с использованием js. А на чистом yii2 с php нет нигде.

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js