Грамотно ли организована структура MVC, самописный MVC-движок

NewEXE

Новичок
Доброго времени суток! Делаю свой движок с нуля (проект маленький, да и самому интересно).
Обычный MVC: роутер, запросы направляются на главный файл /index.php, там $router->run(), в котором вызывается контроллер и экшн (например, в site.ru/site/index управление передается в SiteController, actionIndex с параметрами GET, POST и FILES, если переданы).

Есть два варианта проектирования моделей и контроллеров, как мне кажется, оба приемлемы. Покажу на мини примере регистрации юзера.

Вариант 1. Создать приватные поля в модели, контроллер создаёт и использует объект модели для изменения состояния и передачи его на вид
PHP:
/* Модель */
<?php
class User
{
    const PIN_LENGTH = 4;

    private $id_user;
    private $email;
    private $password;
    private $pin;
    private $power;
    private $pseudo_balance;
    private $role;
   
    function __construct()
    {
         //сложный конструктор, изменяющий состояние объекта в зависимости от кол-ва параметров, с которыми вызван (иначе сказано: несколько конструкторов преобразования)
    }


         public function register() //метод публичный нестатический, изменяет состояние объекта (присваивается id_user, вставленный в базу)
    {
        $hashedPassword = password_hash($this->password, PASSWORD_DEFAULT);
        $sql = 'INSERT INTO User (email, password, pin)'
                .' VALUES (:email, :password, :pin)';

        $result = $GLOBALS['DBH']->prepare($sql);
        $result->bindParam(':email', $this->email, PDO::PARAM_STR);
        $result->bindParam(':password', $hashedPassword, PDO::PARAM_STR);
        $result->bindParam(':pin', $this->pin, PDO::PARAM_STR);
        if($result->execute())
        {
            $this->id_user = $GLOBALS['DBH']->lastInsertId();
            return $this;
        }
        return false;
    }
   
    //ещё методы, проверка email, password и т.п.
}

/* Контроллер */

class UserController
{
    public function actionSignup($params) //params - обезвреженый массив с данными GET, POST, FILES
    {
        $email = false;
        $password = false;
        $pin = false;
       
        $submit = false;
        extract($params['post'], EXTR_IF_EXISTS);
       
        $result = false;
        if(!empty($submit))
        {
            $user = new User($email, $password, $pin);
            $errors = array($user->checkEmail(), $user->checkPassword(), $user->checkPin()); //методы возвращают текст ошибок или пустую строку
            $errors = array_filter($errors);

            if(empty($errors))
            {
               
                $user->setUserData($email, $password, $pin)
                $result = $user->register();
                if($result === false)
                {
                    $errors[] = 'Что-то пошло не так при записи в базу данных. Сообщите администратору';
                }
            }
        }

        // Подключаем вид
        require_once(ROOT.'/views/user/signup.php');
        return true;
    }
Вариант 2. Модель содержит только статические методы, не содержит полей вообще, контроллер не создаёт объект и обращается через :: к методам модели
PHP:
/* Модель */

class User
{
    const PIN_LENGTH = 4;

    //конструктора вообще нет, т.к. объект модели не создаётся

         public static function register($email, $password, $pin) //метод публичный статический, никак не изменяет состояние объекта
    {
        $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
        $sql = 'INSERT INTO User (email, password, pin)'
                .' VALUES (:email, :password, :pin)';

        $result = $GLOBALS['DBH']->prepare($sql);
        $result->bindParam(':email', $email, PDO::PARAM_STR);
        $result->bindParam(':password', $hashedPassword, PDO::PARAM_STR);
        $result->bindParam(':pin', $pin, PDO::PARAM_STR);
        if($result->execute())
        {
            $id_user = $GLOBALS['DBH']->lastInsertId();
            return $id_user; //в случае успеха вернуть вставленный в базу id
        }
        return false;
    }
   
    //ещё методы, проверка email, password и т.п.
   
}

/* Контроллер */

class UserController
{

    public function actionSignup($params) //params - обезвреженый массив с данными GET, POST, FILES
    {
        $email = false;
        $password = false;
        $pin = false;
       
        $submit = false;
        extract($params['post'], EXTR_IF_EXISTS);
       
        $result = false;
        if(!empty($submit))
        {
            $errors[] = User::checkEmail($email);
            //...и т.д: checkPassword, CheckPin.

            if(empty($errors))
            {
               
                $result = User::register($email, $password, $pin);
                if($result === false)
                {
                    $errors[] = 'Что-то пошло не так при записи в базу данных. Сообщите администратору';
                }
            }
        }

        // Подключаем вид
        require_once(ROOT.'/views/user/signup.php');
        return true;
    }

Вопрос: в какой ситуации что лучше сделать? И почему? Любая критика приветствуется, я хочу научиться хорошо программировать. Заранее спасибо
 

fixxxer

К.О.
Партнер клуба
Второе - это вообще не модель, это процедурное программирование, с таким же успехом можно писать функции типа registerUser(), никакой разницы нет.

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

Вменяемый активрекорд тут, кстати, сделать несложно: избавься от $GLOBAL['dbh'], сделай так, чтобы писать $this->database. И сделай такое API, чтобы в контроллере было типа:

PHP:
$user = new User(...);
if (!$user->validate()) {
    $errors = $user->errors();
}
$user->save();
Наверняка понадобится сделать так, что class User extends ActiveRecord ;)

И вообще посмотри, как это сделано в фреймворках.
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
>//params - обезвреженый массив
чочо? дихлофосом побрызгано?

> extract($params['post'], EXTR_IF_EXISTS);
установи себе нормальную IDE (скажем, phpstorm) - сразу увидишь, почему так делать никогда не надо
 

c0dex

web.dev 2002-...
Команда форума
Партнер клуба
/me увидел там $GLOBALS и ушел...
 
Сверху