Как добавить поле к профилю пользователя в OFFLINE Mall. OctoberCMS

Понадобилось добавить телефон к профилю пользователя в Mall. Я смекнул, что Mall использует плагин Rainlab.User, и нужно просто экстендом добавить поле, но оказалось не так всё просто.

Разработчики советуют добавлять поле через событие mall.customer.afterSignup — https://github.com/OFFLINE-GmbH/oc-mall-plugin/issues/172

Но способ плохой, т.к. таким образом не получится изменять телефон из личного кабинета на фронте, а это важно.

Собственно, как мы знаем из документации, чтобы добавить поле в существующий плагин, нам необходимо:

Первое. Создать свой плагин, через который будем расширять Rainlab.User
php artisan plugin:create Author.MallExtend
Второе. Добавить поле в базу, для этого создаём миграцию:

/plugins/author/mallextend/updates/table_users_add_phone_field.php

<?php namespace Author\MallExtend\Updates;

use Schema;
use October\Rain\Database\Schema\Blueprint;
use October\Rain\Database\Updates\Migration;

class TableUsersAddPhoneField extends Migration
{
    const TABLE_NAME = 'users';

    public function up()
    {
        if (!Schema::hasTable(self::TABLE_NAME) || Schema::hasColumn(self::TABLE_NAME, 'phone')) {
            return;
        }

        Schema::table(self::TABLE_NAME, function (Blueprint $obTable) {
            $obTable->string('phone')->nullable();
        });
    }

    public function down()
    {
        if (!Schema::hasTable(self::TABLE_NAME) || !Schema::hasColumn(self::TABLE_NAME, 'phone')) {
            return;
        }

        Schema::table(self::TABLE_NAME, function (Blueprint $obTable) {
            $obTable->dropColumn(['phone']);
        });
    }
}
Третье. В методе boot() нашего плагина добавить поле в контроллер и модель.

/plugins/author/mallextend/Plugin.php

use RainLab\User\Controllers\Users;
use RainLab\User\Models\User;

...

public function boot()
{
    // Добавим поле phone в контроллер, чтобы оно появилось в backend-форме в админке
    Users::extendFormFields(function($form, $model, $context) {
        if (!$model instanceof User) {
            return;
        }

        $form->addFields([
            'phone' => [
                'label'    => 'Phone',
                'required' => true,
                'span'     => 'auto'
            ],
        ]);
    });

    // Добавим поле в модель User
    User::extend(function ($model) {
        $model->addFillable(['phone']); // сделаем поле fillable, иначе оно не будет сохраняться
        $model->rules['phone'] = 'required'; // добавим правило валидации
    });
}
Четвертое. Добавить поле в форму

/theme/default/partials/customerprofile/default.htm

...

<div class="mall-form-control">
    <label for="phone">Phone</label>
    <input id="phone" type="phone" name="phone" value="{{ __SELF__.user.phone }}">
    <div data-validate-for="phone"></div>
</div>

...

Казалось бы всё! Сделали. Но нет.

В момент регистрации пользователя мы получим ошибку валидации, Октябрь будет бесконечно ругаться, что поле phone обязательно для заполнения. Даже если мы его заполнили.

Данные, которые мы отправили
Ошибка валидации

Если убрать правило валидации, то при регистрации пользователя или обновлении профиля с фронта, поле никогда не запишется в базу.

А происходит это из-за того, что при регистрации/сохранении профиля, между Rainlab.User и формой есть компонент CostumerProfile от Mall, который получает все поля формы, но в модель User отправляет только вот эти:

plugins/offline/mall/components/CustomerProfile.php

DB::transaction(function () use ($data) {
    $this->user->customer->firstname = $data['firstname'];
    $this->user->customer->lastname  = $data['lastname'];
    $this->user->name                = $data['firstname'];
    $this->user->surname             = $data['lastname'];
    $this->user->email               = $data['email'];
    if ($data['password']) {
        $this->user->password              = $data['password'];
        $this->user->password_confirmation = $data['password_repeat'];
        $this->user->customer->is_guest    = false;

        $this->user->groups()->detach(UserGroup::getGuestGroup());
    }
    $this->user->save();
    $this->user->customer->save();
});

Что делать?

Я придумал вот такое решение. Пред тем, как модель User будет сохранена, получим данные отправленные с формы и добавим в аттрибут phone модели пользователя данные из формы:

/plugins/author/mallextend/Plugin.php

...
    
    // Добавим поле в модель User
    User::extend(function ($model) {
        $model->addFillable(['phone']); // сделаем поле fillable, иначе оно не будет сохраняться
        $model->rules['phone'] = 'required'; // добавим правило валидации
        

        $model->bindEvent('model.beforeValidate', function() use ($model) {
            if (Input::has('phone')) {
                $phone = Input::get('phone');
                $model->phone = $phone;
            }
        });
    });

...

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *