Задать вопрос
myks92
@myks92
Нашёл решение — пометь вопрос ответом!

Рефакторинг. Как лучше присваивать рейтинг пользователю?

Всем привет!

У меня реализованы рейтинги на сайте. За определённые действия пользователя я присваиваю ему рейтинг. Есть несколько модулей yii: мероприятия, назначения, новости... В каждом модуле есть соответствующие модели AR

Чтобы сейчас присвоить рейтинг пользователю я использую поведения от Yii, которое подключаю в модели AR. В поведении добавляю новый метод который будет обрабатывать событие (пока что использую стандартные события модели AR). В методе поведения я размещаю всю основную логику присвоения, удаления рейтинга. Если логика отрабатывает, то я, с помощью метода модели RatingUser add(), записываю рейтинг в базу и увеличиваю счетчик рейтинга пользователя.

Сейчас мне хотелось бы оптимизировать код в соотвествии с принципом единой отвественности (каждый объект отвечает за то, что должен отвечать). Прошу у вас помощи и советов по данной задаче.

Когда начинаю оптимизировать код — голова кругом)) Возникает много вопросов....

1. Может быть лучше подписываться на свои события не используя поведения?
2. Может что-то вынести в отдельные классы и наследовать их в каждом модуле?
3. Либо вынести в компонент?

Кроме того нужно ведь предусмотреть на скорое будущее... Рейтинг может быть не только у пользователей, но у постов, мероприятий, клубов... Логика примерно будет одинаковой.

Сейчас покажу как всё устроено...

Таблица rating_user
id|user_id|value|rating_type_id|table|record_id|created_at

Где,
user_id - ИД пользователя
value - значение рейтинга
rating_type_id - тип рейтинга
table - имя таблицы, по которой присвоен рейтинг (для отката рейтинга при удалении)
record_id - ИД записи, за которую был присвоен рейтинг (для отката рейтинга при удалении)
created_at - дата и веремя присвоения

Сейчас я записываю рейтинг через события используя поведения...

Подключение
'RatingUser' => [
 *        'class' => 'backend\modules\user\behaviors\RatingUser',
 *    ],


Поведение:
class RatingUserBehaviors extends Behavior
{
//Подписываемся на события
public function events(): array
	{
		return ArrayHelper::merge(parent::events(), [
			Appointment::EVENT_AFTER_INSERT => 'appointmentAdd',
			Appointment::EVENT_AFTER_UPDATE => 'appointmentAdd',
			Appointment::EVENT_AFTER_DELETE => 'appointmentDel',
			Event::EVENT_AFTER_INSERT => 'eventAdd',
			Event::EVENT_AFTER_DELETE => 'eventDel',
		]);
	}

/**
	 * Добавить рейтинг за назначения
	 *
	 * @param $event
	 * @return bool
	 */
	public function appointmentAdd($event)
	{
		/** @var Appointment $model */
		$model = $event->sender;
		$certification = $model->certification;
		$user_id = $certification->user_id;
		$table = $model::tableName();
		$record_id = $model->id;
		
		//Если назначение подтверждено
		if ($model->isConfirmed) {
			if ($certification->role == 'chief_judge') {
				if ($model->isAppointed) {
					$this->model->add($user_id, 4, $table, $record_id);
				} else if ($model->status == $model::STATUS_REFUSED) {
					$this->model->add($user_id, 5, $table, $record_id);
				}
			} else if ($certification->role == 'judge') {
				if ($model->status == $model::STATUS_APPOINTED) {
					$this->model->add($user_id, 1, $table, $record_id);
				} else if ($model->status == $model::STATUS_REPLASED) {
					$this->model->add($user_id, 2, $table, $record_id);
				} else if ($model->status == $model::STATUS_REFUSED) {
					$this->model->add($user_id, 3, $table, $record_id);
				}
			}
		}
		
		return true;
	}
}


Модель рейтинга:
/**
 * This is the model class for table "rating_user".
 *
 * @property int $id
 * @property int $user_id
 * @property int $value
 * @property int $rating_type_id
 * @property int $table
 * @property int $record_id
 * @property int $created_at
 * @property User $user
 * @property Profile $profile
 */
class RatingUser extends \yii\db\ActiveRecord
{
....
/**
     * Добавить рейтинг пользователю
     *
     * @param int $user_id
     * @param int $rating_type_id
     * @param string|null $table
     * @param int|null $record_id
     * @return bool
     */
    public function add(int $user_id, int $rating_type_id, string $table = null, int $record_id = null)
    {
        $value = $this->getValueType($rating_type_id);
        
        $attributes = [
            'user_id' => $user_id,
            'rating_type_id' => $rating_type_id,
            'table' => $table,
            'record_id' => $record_id,
        ];
        
        if (!$this->getExistsRatingByUser($attributes)) {
            $attributes['value'] = $value;
            $this->attributes = $attributes;
            $this->user->updateCounters(['rating_count' => $value]);
            $this->user->save(false);
            $this->save();
            
            return true;
        }
        
        return false;
    }

  /**
     * Удалить рейтинг у пользователя
     *
     * @param string|null $table
     * @param int|null $record_id
     * @return bool
     * @throws \Throwable
     * @throws \yii\db\StaleObjectException
     */
    public function del(string $table = null, int $record_id = null)
    {
        $ratings = $this::find()->where(['table' => $table, 'record_id' => $record_id])->all();
        
        foreach ($ratings as $rating) {
            $rating->user->updateCounters(['rating_count' => -$rating->value]);
            $rating->user->save();
            $rating->delete();
        }
        return true;
    }
}
  • Вопрос задан
  • 149 просмотров
Подписаться 3 Простой Комментировать
Пригласить эксперта
Ответы на вопрос 2
Minifets
@Minifets
Hello world!!!
Дам небольшой совет. Если хотите делать "как лучше", то стоит смотреть в сторону написания тестов. Это не только улучшит дальнейшую поддержку проекта, но и поможет разобраться со структурой кода. Т.к. плохо структурированный код полноценно покрыть тестами у вас не получится.
И да. Основная цель принципов, это облегчить жизнь разработчика, а не усложнить ее ;).
Ответ написан
webinar
@webinar Куратор тега Yii
Учим yii: https://youtu.be/-WRMlGHLgRg
1. Обратите внимание, что "рейтинг поста" и "рейтинг пользователя" может быть как одинаковой сущностью, так и разными. В зависимости от Вашей конкретной ситуации.
2. Поведения никак не мешают SOLID и в тоже время это не означает, что они нужны в данном конкретном случае. Так как поведения - это фишка yii, иногда кажется что они рушат какие-то базовые принципы, но это не совсем так.

Когда начинаю оптимизировать код — голова кругом)) Возникает много вопросов

Оформляйте их отдельными вопросами и получайте ответы конкретные. В данном случае надо или потратить уйму времени, что бы разобраться с Вашей ситуацией (но тогда это задание, а не вопрос), либо налить воды в общем. Думаю Вам оно не надо. Разбейте вопрос на отдельные конкретные вопросы.

Кроме того нужно ведь предусмотреть на скорое будущее... Рейтинг может быть не только у пользователей, но у постов, мероприятий, клубов

Кому нужно?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы