Извините, если заголовок поста неясен, я постараюсь быть максимально простым, потому что я действительно думаю, что на этот вопрос есть простой ответ.
Прежде всего я добавлю немного кода (не настоящий код, просто для тестирования):
Модель:
class User{
public $id;
public $name;
public $age;
}
Интерфейс:
interface IData{
public function search(User $user)
}
Репозиторий классов:
class DataRepository{
protected $db;
public $conn;
public $data;
// Passing the interface DB to the constructor
public function __construct(DB $db)
{
$this->db = $db;
}
public function search(User $user){
$query = " SELECT * FROM test_db WHERE id_user = ". $user->id . " and name = '%" . $user->name . "%'";
$this->conn = $this->db->connection();
$this->data = $this->conn->query($query);
$dataList = [];
foreach ($this->data as $row)
{
$dataList [] = (object)array(
"name" => $row["name"],
"age" => $row["age"],
);
}
return $dataList;
}
}
Контроллер:
class UserController{
public $Idata;
// Passing the interface IData to the constructor
function __construct(IData $Idata) {
$this->Idata = $Idata;
}
// Function to search the user and return an array with the data
function printData(){
$Mdata = new User();
$Mdata->id = 5;
$Mdata->name = "Carl";
$Mdata->age = 39;
$result = $this->Idata->search($Mdata);
return $result;
}
}
Вид:
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8">
<meta http-equiv = "X-UA-Compatible" content = "IE=edge">
<meta name = "viewport" content = "width=device-width, initial-scale=1.0">
<title>Test Page</title>
</head>
<body>
<?php
// We create an instance of UserController
$UserData = new UserController();
// Print the data
echo "<pre>";
print_r($UserData);
echo "</pre>"
?>
</body>
</html>
Итак, я пытался изучить шаблон репозитория с помощью MVC, чтобы отделить мой уровень БД от контроллера и инкапсулировать, все выглядело отлично, пока мне не понадобилось использовать интерфейс IData на главном контроллере (UserController), на мой взгляд, я была ошибка
$UserData = new UserController() -> returns an error: Expected 1 arguments. Found 0.
Я знаю значение этой ошибки, что для нее требуется параметр (интерфейс), однако кажется странным экземпляр интерфейса на стороне представления, чтобы позже передать его в качестве аргумента конструктору.
Я попытался:
<?php
$IData = new IData();
$UserData = new UserController($IData);
echo "<pre>";
print_r($UserData);
echo "</pre>"
?>
он возвращает эту ошибку: Fatal error: Uncaught Error: Cannot instantiate interface IData.
Мой главный вопрос: есть ли способ использовать интерфейс только в конструкторе UserController без необходимости определять его в представлении?
если это невозможно (наверняка будет так), что мне делать? (я не могу использовать ключевое слово implemante, потому что я пытаюсь использовать шаблон репозитория, делая внедрение зависимостей из конструктора)
Согласитесь, очень неясно, почему вы пытаетесь создать экземпляр контроллера из представления. MVC работает совсем не так. Механизм маршрутизации должен создавать экземпляр контроллера с помощью системы внедрения зависимостей для создания подходящих объектов для передачи в качестве параметров конструктору контроллера.
Не имеет отношения к текущей проблеме, но SELECT * FROM test_db WHERE id_user = ". $user->id . " and name = '%" . $user->name . "%' странно. 1. Значения должны быть связаны 2. Если есть идентификатор пользователя, зачем вам имя 3. Почему в имени есть подстановочные знаки или это опечатка для оператора (например, = должно быть like)?
@ М.Эрикссон, окей, спасибо! Я попробую. У меня есть вопрос, вы сказали, что мне следует избегать использования экземпляров в представлении, если я не использую экземпляр для вызова контроллера, как я могу получить данные? Должен ли я просто указать, когда я хочу получить окончательный результат?
@ADyson Механизм маршрутизации, вы имеете в виду, что я должен создать экземпляр контроллера из маршрута? (получить, опубликовать ..)
@user3783243 user3783243 не особо задумывался над запросом, поэтому просто игнорируйте его :)
большое спасибо за комментарий, я ценю всех вас за то, что вы показываете мне мои ошибки, видите их и учитесь
i should instantiate a controller from a route...по сути да. Обычно в архитектурах MVC механизм маршрутизации обрабатывает все запросы, поступающие в приложение, проверяет URL-адрес и, исходя из этого, использует свои правила маршрутизации для определения того, какой контроллер и метод действия необходимо вызвать. например в относительно часто используемой структуре, если URL-адрес https://yoursite.com/user/printData, тогда должно сработать, что ему нужно создать экземпляр пользовательского контроллера и запустить функцию printData. Конечно, большинство систем позволяют разработчику также переопределять значения по умолчанию с помощью настраиваемых URL-адресов маршрутов.
Вы потратили какое-то время на изучение существующих реализаций MVC, чтобы увидеть, что они делают?
Я не понимаю, почему использование ключевого слова implement помешает вам использовать внедрение зависимостей. Интерфейсы PHP требуют, чтобы класс явно объявлял, какой интерфейс он реализует.
@ADyson Okei, теперь я понял. Я использовал Laravel для создания некоторых проектов, и этот тоже использует систему маршрутов, вызывая контроллер и определяя имя маршрута (не знал, что маршруты играют огромную роль в архитектуре MVC).
@ADyson Я изучил несколько примеров шаблона репозитория, однако не нашел много примеров в контроллере - часть просмотра.
"на стороне просмотра" - сам PHP ничего не знает о "стороне просмотра", и я даже не вижу, как данная ошибка (что вы не можете создать экземпляр интерфейса) связана с MVC в конце концов
@NicoHaase Model-View-Controller -> Я говорю о представлении, где у меня есть весь мой html и прочее
@NicoHaase Конструктору UserController требуется параметр (интерфейс), поэтому я попытался создать экземпляр класса UserController в представлении и указать интерфейс в качестве параметра (в соответствии с архитектурой MVC). Я знаю, и я сказал это, я не знал, что создание экземпляра объекта в представлении было неправильным.






Это больше вопрос о том, как в PHP выполняется внедрение зависимостей.
Прежде всего, вы должны исправить свой код, объявив DataRepository реализует интерфейс:
class DataRepository implements IData
{
// ...
}
Нет никакого способа обойти это. PHP требует явного объявления этого.
Затем подумайте о следующем:
UserController зависит от IData.DataRepository должен реализовать IData.UserController ему нужна реализация IData. Но их может быть больше одного.IData использовать для UserController?Для (4) вам понадобится способ объявить это либо:
все классы, которые хотят IData, должны иметь DataRepository; или
некоторые из классов, которые хотят IData, например UserController, должны иметь DataRepository.
Простой способ — использовать контейнер внедрения зависимостей, такой как PHP-DI. Они гибки в объявлении рецептов того, как должна выполняться зависимость интерфейса.
Предполагая, что у вас установлен «php-di/php-di» с composer, тогда вы должны запустить этот фрагмент кода в каком-то общем php/ядре.
require_once __DIR__ . '/vendor/autoload.php';
$db = getDB(); // some way to create the database object
$container = new DI\Container();
$container->set(IData::class, \DI\create(DataRepository::class));
$container->set(DB::class, \DI\value($db));
// ...
Если вы передадите переменную $container в файл представления, вы можете просто сделать это:
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8">
<meta http-equiv = "X-UA-Compatible" content = "IE=edge">
<meta name = "viewport" content = "width=device-width, initial-scale=1.0">
<title>Test Page</title>
</head>
<body>
<?php
// We create an instance of UserController
$UserData = $container->get(UserController::class);
// Print the data
echo "<pre>";
print_r($UserData);
echo "</pre>"
?>
</body>
</html>
Обратите внимание, что вам не нужно вручную создавать зависимости UserController. При запросе UserController контейнер здесь будет искать все рецепты, чтобы выполнить все зависимости, требуемые UserController. Таким образом, представлению не нужно знать детали реализации контроллера.
Как я упоминал ранее, вам может понадобиться использовать DataRepository вместо IData только для некоторых контроллеров. В PHP-DI и других контейнерах внедрения зависимостей есть способы указать рецепт для создания DataRepository, чтобы сделать это.
Для PHP-DI вы должны прочитать документацию для этого или для других более сложных способов использования.
Я предполагаю, что более распространенный дизайн MVC, вероятно, будет иметь вызов контроллера для представления. И создайте экземпляр контроллера (с использованием контейнера) в качестве деталей реализации:
Маршрутизация:
// Pseudo-code only
// But all MVC framework has its own router / routing code like this
// some container initialization
$container = dummyGetContainer();
// router will probably need container
$router = new MyRouter($container);
// register the handler of GET request to "/userData"
$router->get('/userData', 'UserController::viewData');
// actually execute the routing
// internally will do:
// 1. find the handler of the request method and path
// 2. instantiate the controller with dependencies.
// 3. execute the handler against the request.
$router->route(dummyGetRequestFromEnvironment());
Контроллер:
class UserController{
public $Idata;
// Passing the interface IData to the constructor
function __construct(IData $Idata) {
$this->Idata = $Idata;
}
function viewData($request) {
$userData = $this->getData();
include __DIR__ . '/../views/userDataView.php';
}
// Function to search the user and return an array with the data
function getData(){
$Mdata = new User();
$Mdata->id = 5;
$Mdata->name = "Carl";
$Mdata->age = 39;
$result = $this->Idata->search($Mdata);
return $result;
}
}
Вид:
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8">
<meta http-equiv = "X-UA-Compatible" content = "IE=edge">
<meta name = "viewport" content = "width=device-width, initial-scale=1.0">
<title>Test Page</title>
</head>
<body>
<pre>
<?php print_r($userData); ?>
</pre>
Этот дизайн даже лучше, потому что представление не должно знать ничего о контроллере. Он должен просто представлять данные, которые ему даются.
Я никогда не слышал о PHP-DI, я проверю его и начну использовать маршруты. Спасибо за хорошо объясненный пример
Код маршрутизатора, который я написал здесь, является только псевдокодом. Вам нужно будет написать его или найти его реализацию. Фреймворки MVC, такие как Laravel и Symfony, имеют свой собственный способ указания маршрутов. И они оба имеют свою собственную реализацию контейнера для инъекций зависимостей.
function __construct(IData $Idata)- Это означает, что вам нужно отправить объект, реализующий интерфейс. ВашDataRepository-класс должен реализовать этот интерфейс, а затем вы передаете экземплярDataRepositoryсвоемуUserController. Вам также следует избегать создания экземпляров в представлениях. Ваш контроллер должен получать все данные, необходимые представлению, и просто передавать данные представлениям. Представления не должны знать, откуда они берутся или как их получить.