maksim92
@maksim92
Нашел решение — пометь вопрос ответом!

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

Всем привет!

У меня реализованы рейтинги на сайте. За определённые действия пользователя я присваиваю ему рейтинг. Есть несколько модулей 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;
    }
}
  • Вопрос задан
  • 140 просмотров
Пригласить эксперта
Ответы на вопрос 2
Minifets
@Minifets
Hello world!!!
Дам небольшой совет. Если хотите делать "как лучше", то стоит смотреть в сторону написания тестов. Это не только улучшит дальнейшую поддержку проекта, но и поможет разобраться со структурой кода. Т.к. плохо структурированный код полноценно покрыть тестами у вас не получится.
И да. Основная цель принципов, это облегчить жизнь разработчика, а не усложнить ее ;).
Ответ написан
webinar
@webinar Куратор тега Yii
Учим yii: https://youtu.be/-WRMlGHLgRg
1. Обратите внимание, что "рейтинг поста" и "рейтинг пользователя" может быть как одинаковой сущностью, так и разными. В зависимости от Вашей конкретной ситуации.
2. Поведения никак не мешают SOLID и в тоже время это не означает, что они нужны в данном конкретном случае. Так как поведения - это фишка yii, иногда кажется что они рушат какие-то базовые принципы, но это не совсем так.

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

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

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

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

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

Войти через центр авторизации
Похожие вопросы
Платформа НТИ Москва
от 160 000 до 190 000 руб.
от 30 000 до 50 000 руб.
Alma Innovation Group Алматы
от 50 000 до 90 000 руб.