Понадобилось добавить телефон к профилю пользователя в 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
Второе. Добавить поле в базу, для этого создаём миграцию:
<?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() нашего плагина добавить поле в контроллер и модель.
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'; // добавим правило валидации
});
}
Четвертое. Добавить поле в форму
...
<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 обязательно для заполнения. Даже если мы его заполнили.
![](https://deadblog.ru/wp-content/uploads/2020/12/100842750-e28ee680-34ab-11eb-806f-94a353af9518-500x171.png)
![](https://deadblog.ru/wp-content/uploads/2020/12/100842787-ecb0e500-34ab-11eb-8c3a-9b6afbfe6db3-500x125.png)
Если убрать правило валидации, то при регистрации пользователя или обновлении профиля с фронта, поле никогда не запишется в базу.
А происходит это из-за того, что при регистрации/сохранении профиля, между Rainlab.User и формой есть компонент CostumerProfile от Mall, который получает все поля формы, но в модель User отправляет только вот эти:
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 модели пользователя данные из формы:
...
// Добавим поле в модель 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;
}
});
});
...