Пожалуйста, критикуйте мою первую попытку MVC на PHP

Что ж, я не большой специалист по фреймворкам, но мне нравилось то, что я слышал обо всем движении MVC, поэтому я подумал, что попробую создать простое приложение на моем языке выбора (PHP).

Думаю, вопрос в том, где я ошибся? Я знаю, что есть много споров о том, насколько толстым должен быть контроллер / модель, поэтому, надеюсь, мы сможем этого избежать, однако мне особенно любопытно ваши мысли о том, как я вписываюсь в Datatier.

Также я купил домен, чтобы провести некоторое тестирование, поэтому, если вы хотите увидеть его в действии, вы можете перейти на www.omgmvc.com

Во-первых, вот моя схема базы данных:

CREATE TABLE `movies` (
  `id` int(11) NOT NULL auto_increment,
  `movie_name` varchar(255) NOT NULL,
  `release_date` date NOT NULL,
  `directors_name` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
);

INSERT INTO `movies` VALUES (1,'Star Wars', '1977-05-25', 'George Lucas');
INSERT INTO `movies` VALUES (2,'The Godfather', '1972-03-24', 'Francis Ford Coppola');
INSERT INTO `movies` VALUES (3,'The Dark Knight', '2008-07-18', 'Christopher Nolan');

А вот файлы:

index.php(контроллер)

<?php

include('datatier.php');
include('models/m_movie.php');

if (isset($_GET['movie']) && is_numeric($_GET['movie']))
{
    $movie = new Movie($_GET['movie']);

    if ($movie->id > 0)
    {
        include('views/v_movie.php');
    }
    else
    {
        echo 'Movie Not Found';
    }
}
else
{
    $movies = Movie::get_all();

    include('views/v_list.php');
}

?>

datatier.php(уровень данных)

<?php

class DataTier
{
    private $database;

    function __construct()
    {
        $this->connect();
    }

    function __destruct()
    {
        $this->disconnect();
    }

    function connect()
    {
        $this->database = new PDO('mysql:host=localhost;dbname=dbname','username','password');
    }

    function disconnect()
    {
        $this->database = null;
    }

    function get_all_from_database($type)
    {
        $database = new PDO('mysql:host=localhost;dbname=dbname','username','password');

        switch ($type)
        {
            case 'movie':
                $query = 'SELECT id FROM movies';
                break;
        }

        $movies = array();

        foreach ($database->query($query) as $results)
        {
            $movies[sizeof($movies)] = new Movie($results['id']);
        }

        $database = null;

        return $movies;
    }

    function get_from_database($type,$id)
    {
        switch ($type)
        {
            case 'movie':
                $query = 'SELECT movie_name,release_date,directors_name FROM movies WHERE id=?';
                break;
        }

        $database_call = $this->database->prepare($query);
        $database_call->execute(array($id));

        if ($database_call->rowCount() > 0)
        {
            return $database_call->fetch();
        }
        else
        {
            return array();
        }
    }
}

?>

модели / m_movie.php(модель)

<?php

class Movie extends DataTier
{
    public $id;
    public $movie_name;
    public $release_date;
    public $directors_name;

    function __construct($id)
    {
        parent::connect();

        $results = parent::get_from_database('movie',$id);

        if ($results == array())
        {
            $this->id = 0;
        }
        else
        {
            $this->id = $id;
            $this->movie_name = $results['movie_name'];
            $this->release_date = $results['release_date'];
            $this->directors_name = $results['directors_name'];
        }
    }

    function __destruct()
    {
        parent::disconnect();
    }

    static function get_all()
    {
        $results = parent::get_all_from_database('movie');

        return $results;
    }
}

?>

просмотры / v_list.php(Посмотреть)

<html>
    <head>
        <title>Movie List</title>
    </head>
    <body>
        <table border = "1" cellpadding = "5" cellspacing = "5">
            <thead>
                <tr>
                    <th>Movie Name</th>
                    <th>Directors Name</th>
                    <th>Release Date</th>
                </tr>
            </thead>
            <tbody>
<?php foreach ($movies as $movie) { ?>
                <tr>
                    <td><a href = "/?movie=<?php echo $movie->id; ?>"><?php echo $movie->movie_name; ?></a></td>
                    <td><?php echo $movie->directors_name; ?></td>
                    <td><?php echo $movie->release_date; ?></td>
                </tr>
<?php } ?>
            </tbody>
        </table>
    </body>
</html>

просмотры / v_movie.php(Посмотреть)

<html>
    <head>
        <title><?php echo $movie->movie_name; ?></title>
    </head>
    <body>
        <h1><?php echo $movie->movie_name; ?></h1>
        <h2>Directed by <?php echo $movie->directors_name; ?></h2>
        <h3>Released <?php echo $movie->release_date; ?></h3>
    </body>
</html>

Хороший вопрос, но я бы предположил, что это действительно относится к codereview.

GordonM 27.03.2012 23:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
15
1
2 088
3

Ответы 3

Во-первых, вы неплохо справляетесь с разделением вещей. Это окупается в будущем, так что не сдавайтесь.

Макет базы данных (или даже сама база данных) не имеет отношения к сути MVC. В большинстве случаев это реляционная база данных, однако MVC не требует этого явно (вы также можете использовать хранилище XML или какую-то сетку / облако). Что важно для MVC, так это отделить модель от остального, что вы и сделали.

Ваш взгляд также четко отделен от остальных. Подобно части M MVC, представления могут представлять не только HTML, но и любой текстовый вывод (XML, XML + XSL, RSS, обычный текст или даже сообщения электронной почты). Представления могут быть реализованы несколькими способами: PHP включает в себя, как ваш , шаблоны (например, Smarty) или полноценные объекты, сериализуемые в текст. Я далек от того, чтобы судить, какая стратегия лучше, это вопрос индивидуального стиля программирования и требований проекта.

Ваш контроллер сбивает с толку (это скорее контроллер страницы, чем контроллер приложения). Вероятно, это связано с тем, что в архитектуре MVC есть одна скрытая часть. Он называется Передний Контроллер или Диспетчер. Диспетчер должен проанализировать ввод, создать экземпляр контроллера (как в Application Controller) и вызвать запрошенный метод. Если вы хотите продолжить свою собственную реализацию MVC, я предлагаю вам использовать какой-нибудь общий способ передачи класса контроллера и имени метода в URL-адресе, т.е.

index.php/Movies/list
index.php/Movies/details/35

Затем в новом index.php вы просто анализируете $ _SERVER ['PATH_INFO'], создаете экземпляр класса Movies и вызываете его метод list, т.е.

$args = explode('/', ltrim($_SERVER['PATH_INFO'], '/'));
$className = array_shift($args);
$method = array_shift($args);
require "$className.php";
call_user_func_array(array(new $className(), $method), $args);

Затем вы просто перемещаете содержимое блока if-else в два отдельных метода в классе Movies.

class Movies { // may extend generic Controller class if you wish

    public function list() {
        $movies = Movie::get_all();
        include 'views/v_list.php';
    }

    public function details($movieId) {
        $movie = new Movie($movieId);
        if ($movie->id > 0) {
            include 'views/v_movie.php';
        } else {
            echo "Movie Not Found";
    }

}

Таким образом, у вас может быть несколько контроллеров, каждый с несколькими действиями.

Заключительные замечания.

  • На стороне базы данных было бы удобно использовать одну из существующих структур ORM. Они сэкономят вам дни работы и, вероятно, будут работать лучше, чем уровень db, созданный вручную. Я также предлагаю обрабатывать создание экземпляра PDO, поскольку создание экземпляра PDO в каждом объекте модели - не самый чистый способ. Что-то вроде DBFactory::getConnection подойдет.

  • Вы можете рассмотреть возможность возврата HTML, а не повторения его в контроллере. Это дает вам большую гибкость, если вы хотите реализовать Перехватывающие фильтры, который будет оборачивать контроллер, перехватывать его вывод и предварительно или постобработать его. Очень удобно иметь фильтр, который автоматически присоединяет верхний и нижний колонтитулы HTML.

  • Создание пользовательского фреймворка - это большое развлечение и ценный образовательный опыт, однако я бы посоветовал использовать один из существующих фреймворков для более серьезных задач.

Всего наилучшего.

+1 за использование существующей ORM - попытка сопоставить классы с реляционной моделью самостоятельно - это боль

Wickethewok 15.01.2009 18:48

Спасибо за хороший пост

Andrew G. Johnson 15.01.2009 23:31

У вас все хорошо, но у меня есть несколько предложений:

  1. Поскольку вы используете php5, не забывайте о функции __autoload.
  2. Лучше назвать свой уровень данных Model.
  3. get_all_from_database не объявлен как статический, но вы вызываете его статически, это генерирует предупреждение уровня E_STRICT. Установить error_reporting (E_ALL | E_STRICT); и вы должны увидеть предупреждение.
  4. Статическая функция get_all () должна быть в классе Model (в вашем случае Datatier), чтобы вам не приходилось переписывать ее для каждой другой модели. Единственное изменение, которое вам нужно сделать в этой функции, - это заменить строку:

    $ results = parent :: get_all_from_database ('фильм');

с участием

$results = $this->get_all_from_database(get_class($this));

это предполагает, что имя ваших моделей должно совпадать с именами ваших таблиц в базе данных.

Много хороших моментов. Спасибо, Джоуи!

Andrew G. Johnson 15.01.2009 21:21

Я удивлен, что никто больше не голосует, я нашел это действительно хорошим ответом

Andrew G. Johnson 15.01.2009 23:50

Единственное, что сразу бросается мне в глаза (это еще не было упомянуто) как "странное", это то, что вы используете два экземпляра PDO для связи с одной и той же базой данных. Неплохо само по себе, но вы также дважды сохраняете имя пользователя, пароль и остальную часть dsn.

Это может быть просто потому, что это образец кода, размещенный в сети.

Другие вопросы по теме