Лучшая практика для функции «Нравится»

Я работаю над веб-сайтом для флеш-игр. У меня есть две модели: Игра и Пользователь, а также промежуточная таблица, в которой я храню действия пользователя, например: «Пользователю 1 нравится игра 3».

  1. Где лучшее место для моей функции лайка?
  2. Является ли хорошей практикой получение текущего идентификатора пользователя в модели Game? или я должен передать его как параметр?

Из соображений производительности я также увеличиваю поле «Нравится» в игровой таблице. Я не стал проверять, нравится ли игра пользователю, просто для простоты.
Вот мои варианты:

Первая версия:

$user->like(12345);

class User  
{
  public function like($game_id)
  {
    $like = new User_Game();  
    $like->user_id = $this->id;  
    $like->game_id = $game_id;  
    $like->save();  

    $obj = new Game($game_id);  
    $obj->likes++;  
    $obj->save();  
  }
}  

Вторая версия:

$game->like(); // by current user

class Game  
{  
  public function like()  
  {

    $like = new User_Game();    
    $like->user_id = $_SESSION[‘user_id’];    
    $like->game_id = $this->id;    
    $like->save();   

    $this->likes++;    
    $this->save();    
  }    
}    

person igs013    schedule 18.09.2012    source источник
comment
Is it a good practice to grab current user id in Game model? Нет. or should I pass it as parameter? Да. Подумайте об этом, прямо сейчас вы сохраняете идентификатор своего активного пользователя в сеансе, что произойдет, если позже вы сохраните его где-то еще? Что еще более важно, ваша функция может использоваться только для текущего активного пользователя, вы не можете like() для любого другого пользователя. Идентификатор пользователя является внешней зависимостью, нет абсолютно никаких причин привязывать к нему вашу игровую модель, прочтите зависимость инъекции.   -  person yannis    schedule 18.09.2012


Ответы (1)


Честно говоря, я не уверен, что это лучшее место для такого вопроса. Возможно, codereview подходит лучше. Кроме всего прочего, ИМО, ни один из двух вариантов, которые вы предлагаете, не является "лучшим подходом". Но, как всегда, это может быть личным делом.
На мой взгляд, лучший способ реализовать ООП — это как можно скорее передать все ваши данные в объекты и реализовать сервисный уровень, который берет на себя операции, требующие нескольких запросов или нескольких объектов.

Если я могу предположить, что вы используете шаблон MVC-ish, ваш контроллер получает данные. Там вы создаете экземпляр объекта Game и устанавливаете идентификатор 123456. Вы можете передать этот экземпляр методу службы с именем fillGameModel(Game $gameInstance). Этот метод подключается к БД, устанавливает все остальные свойства объекта Game и возвращает его. То же самое касается объекта User. Оба этих объекта затем можно передать другому методу службы: likeGame(Game $game, User $user). Этот метод может позаботиться обо всем остальном.
Лично я бы пошел еще дальше и использовал преобразователи для доступа к БД, но сейчас я не буду об этом. Вот пример использования службы и более объектно-ориентированный подход:

//controller:
$user = new User();
$user->setId($_SESSION['user_id']);
$game = new Game();
$game->setId(123456);//wherever you get this from
$service = new MainService();
$service->userLikes($game,$user);

//service:
public function userLikes(Game $game, User $user)
{
    $user = $this->_completeUser($user);
    $game = $this->_completeGame($game);
    //insert or update whatever data you need...
}

protected function _completeUser(User $user)
{
    $db = $this->_getConnection();//asuming PDO, to keep things simple
    $query = 'SELECT * FROM my_db.users WHERE id = ?';
    $stmt = $db->prepare($query);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    foreach ($row as $field => $value)
    {//this implies getters and setters in your model
        $user->{'set'.ucfirst(strtolower($field))}($value);
    }
    return $user;
}

protected function _completeGame(Game $game)
{
    $db = $this->_getConnection();
    $query = 'SELECT * FROM my_db.games WHERE id = ?';
    $stmt = $db->prepare($query);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    foreach ($row as $field => $value)
    {//field id becomes "setId" method, field name "setName" etc...
        $game->{'set'.ucfirst(strtolower($field))}($value);
    }
    return $game;
}

//just for show: a pseudo-overloader method, if your models all share the same
//abstract class. 
protected function _completeAny(Model_Abstract $model)
{
    $modelName = get_class($model);
    if (method_exists($this,'_complete'.$modelName))
    {
        return $this->{'_complete'.$modelName}($model);
    }
    throw new Exception('No completion method for '.$modelName.' found');
}

Опять же, циклы по результирующему набору можно заменить методом в классе абстрактной модели, который принимает массив в качестве аргумента и преобразует имена полей в соответствующие установщики. Много места для абстракции, я бы сказал ;-)

person Elias Van Ootegem    schedule 18.09.2012