Что ж, я не большой специалист по фреймворкам, но мне нравилось то, что я слышал обо всем движении 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>






Во-первых, вы неплохо справляетесь с разделением вещей. Это окупается в будущем, так что не сдавайтесь.
Макет базы данных (или даже сама база данных) не имеет отношения к сути 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 - попытка сопоставить классы с реляционной моделью самостоятельно - это боль
Спасибо за хороший пост
У вас все хорошо, но у меня есть несколько предложений:
Статическая функция get_all () должна быть в классе Model (в вашем случае Datatier), чтобы вам не приходилось переписывать ее для каждой другой модели. Единственное изменение, которое вам нужно сделать в этой функции, - это заменить строку:
$ results = parent :: get_all_from_database ('фильм');
с участием
$results = $this->get_all_from_database(get_class($this));
это предполагает, что имя ваших моделей должно совпадать с именами ваших таблиц в базе данных.
Много хороших моментов. Спасибо, Джоуи!
Я удивлен, что никто больше не голосует, я нашел это действительно хорошим ответом
Единственное, что сразу бросается мне в глаза (это еще не было упомянуто) как "странное", это то, что вы используете два экземпляра PDO для связи с одной и той же базой данных. Неплохо само по себе, но вы также дважды сохраняете имя пользователя, пароль и остальную часть dsn.
Это может быть просто потому, что это образец кода, размещенный в сети.
Хороший вопрос, но я бы предположил, что это действительно относится к codereview.