Php 在MVC中,如何将总是使用的属性从控制器传递给其他类?
我目前正在改进自己的MVC,对于以下场景,我找不到一个好的解决方案: 在我的大多数模型中,我使用的是一些(已经被另一个模型验证过的)基于用户的输入,当然需要将它们从控制器(我基本上告诉模型如何处理输入)传递到各种模型。目前,我正在将每个用户输入放入一个属性:Php 在MVC中,如何将总是使用的属性从控制器传递给其他类?,php,class,model,controller,Php,Class,Model,Controller,我目前正在改进自己的MVC,对于以下场景,我找不到一个好的解决方案: 在我的大多数模型中,我使用的是一些(已经被另一个模型验证过的)基于用户的输入,当然需要将它们从控制器(我基本上告诉模型如何处理输入)传递到各种模型。目前,我正在将每个用户输入放入一个属性: foreach($this->properties as $property => $empty) { if(isset($_POST[$property])) {
foreach($this->properties as $property => $empty)
{
if(isset($_POST[$property]))
{
$this->properties[$property] = htmlspecialchars(trim($_POST[$property]));
}
}
class baseController
{
protected $userProperties;
public function __construct()
{
$this->userProperties = Request::getInstance()->getUserProperties();
}
}
最终,当我需要一个新的模型来做某事时,我这样称呼它:
new view('calendar',$data,$this->properties);
最后,在模型中,我通过将输入/变量放入模型属性中来接收它们
class validation
{
public function __construct($values)
{
foreach($values as $property => $value)
{
$this->{$property} = $value;
}
}
}
这样,我就不必考虑变量来自何处(在用户输入经过验证之后,我真的不在乎了),并且可以随时为我编写一个干净易读的$this->user\u input版本
但我有一种感觉,这不是最简单的方法,也可能不是一种好方法。最让我烦恼的是,在编写新类/模型时,我总是必须告诉模型将输入带入它们自己的属性,并且在调用新类时,我总是必须传递参数
当调用一个新类时,我是否可以通过某种方式从用户那里继承这些变量,而无需将控制器设置为父类?或者,将控制器设置为父类是否有意义?我认为,当另一个控制器使用该模型时,会令人困惑
将控制器设置为父控制器是否有意义
是的,我可能就是这么做的。然后,您可以对要共享/继承的属性使用protected。根据您的需要,将用户输入存储在(例如UserRequest类)的属性中可能是有意义的:
在引导类或路由类中,捕获用户输入时,将其保存在请求实例中,然后让所有控制器扩展读取此属性的基类:
foreach($this->properties as $property => $empty)
{
if(isset($_POST[$property]))
{
$this->properties[$property] = htmlspecialchars(trim($_POST[$property]));
}
}
class baseController
{
protected $userProperties;
public function __construct()
{
$this->userProperties = Request::getInstance()->getUserProperties();
}
}
然后所有控制器都可以访问它,您只需捕获一次 我认为更好的解决方案是将所有输入存储在一个对象中,让我们称之为data
。每个模型都可以具有数据
属性。控制器完成输入验证后,可以将对象传递到第一个模型并存储在那里。此时,可以在模型之间自由传递对象。如果要更改data
中的值,则应稍后使用方法调用更新controllers对象,如$this->data=$Model->GetData()代码>或其他什么
在MVC范例中,让模型访问控制器的属性是不明智的。控制器基本上应该启动所有通信,即控制器将数据传递给对其进行操作的模型,然后控制器请求该数据并将其放入视图中。让控制器直接保存数据和在其上运行的模型不是一个好的做法
最让我烦恼的是,在编写新类/模型时,我总是必须告诉模型将输入带入它们自己的属性,并且在调用新类时,我总是必须传递参数
假设你有两个问题:
重复定义每个类定义的属性
为每个类创建传递参数
从最简单和最基本的意义上讲,你不能回避这两个问题。如果您不告诉类(至少不知何故)它表示哪些属性,它就不会知道。第二点有点类似,如果数据没有设置为类,它将无法工作
因此,从技术上讲,根本不可能防止这两种情况的发生,问题是如何让它变得更舒适,并减少重复——如果可能的话
一种方法是将所有这些对象都设置为同一类型。我的意思是,实际上这些只是一些改进的阵列,不是吗
因此,您可以自己创建一个基类,可以从中进行扩展,该基类包含所有需要的代码,例如导入数组、定义属性
因此,您只需要编写一次代码,并创建任意数量的对象和不同的“类型”
举个例子,让我们创建一个这样的对象,它有一个基类来完成它的工作:
class TestModel extends SelfDefinedVariableObjectBase
{
protected $properties = ['bar' => 'hello world'];
}
就是这样,对象定义。现在让我们使用它:
// $_POST['bar'] = '<h1>test</h1> let\'s throw some HTML in';
$foo = new TestModel($_POST);
echo $foo->bar, "\n";
你现在可能想要这个。因此,您可以创建一些装饰器,例如,这里的装饰器使用回调函数:
class VariableObjectCallbackDecorator
{
private $subject;
private $callback;
public function __construct(VariableObjectBase $object, callable $callback) {
$this->subject = $object;
$this->callback = $callback;
}
public function __get($name) {
return call_user_func($this->callback, $this->subject->__get($name));
}
}
让我们将其用于上一个示例中的测试对象:
$bar = new VariableObjectCallbackDecorator($foo, 'htmlspecialchars');
echo $bar->bar, "\n";
现在这一次的输出是:
<h1>test</h1> let's throw some HTML in
希望这是有帮助的。您可以在这里找到代码:好的,这里要记住的一件大事是,您的控制器“有一个”变量容器(保存所有属性),而控制器是(或“是一个”)变量容器。所以首先,您应该使用组合,而不是继承
下面的代码片段将被注释以更详细地解释。一些注意事项:
- InputData实例可以创建在控制器级别之上(例如,在不同的控制器中,以便可以在多个控制器之间共享),这回答了问题的主要部分。关键的一点是你只写了一次,你可以放心地说,一旦它在那里,它的好去
- 您可以在InputData中包含所有验证方法,因为InputData的作用是安全地存放/存储数据——在我看来,这是一个良好的abtraction级别,或者换句话说,“它负责输入数据,如果输入数据有问题,我知道该去哪里查找”
- 最后,为了增加一些额外的亮点,我添加了一些位操作,以便在通过input_data->add添加值时,可以针对多种类型对它们进行验证(例如,可以添加一些需要同时验证为数字和
require_once( "Controller.php" );
$controller = new Controller();
$controller->print_current_user();
<?
require_once( "Input_Data.php" );
class Controller
{
// Variables
private $input_data;
// Models
// private $model_user;
public function __construct()
{
$this->input_data = new Input_Data();
//$this->model_user = new Model_User();
// Process input (might not happen in the constructor,
// in fact, it might happen higher up, so it can be shared
// Possibly looping over GET / POST data
// for each one, add it to the inputData
$_GET[ 'name' ] = 'Chris';
$_GET[ 'country' ] = 'Australia';
// example iteration 1
$this->input_data->add( 'name', $_GET[ 'name' ], Input_Data::TYPE_VALIDATE_NAME | Input_Data::TYPE_VALIDATE_TEXT );
// example iteration 2
$this->input_data->add( 'country', $_GET[ 'country' ], Input_Data::TYPE_VALIDATE_COUNTRY );
}
// later on in controller, model needs to find the user by name
public function print_current_user()
{
// Example Usage to Model:
// $this->$model_user->get_by_name( $this->input_data->get( 'name' ) );
//
// For now, we'll settle with just printing it out
echo $this->input_data->get( 'name' );
}
}
?>
<?
class Input_Data
{
const TYPE_VALIDATE_TEXT = 0;
const TYPE_VALIDATE_NAME = 1;
const TYPE_VALIDATE_EMAIL = 2;
const TYPE_VALIDATE_ADDRESS = 4;
const TYPE_VALIDATE_COUNTRY = 8;
protected $data;
public function __construct() {
$this->data = array();
}
public function add( $name, $value, $type )
{
if( $type & TYPE_VALIDATE_TEXT )
{
// validate input as text
// if valid, flag as such, to be inserted
// or alternatively return a safe version
// depending on your application, an empty string
}
if( $type & TYPE_VALIDATE_NAME )
{
// validate input as name
}
if( $type & TYPE_VALIDATE_EMAIL )
{
// validate input as email
}
if( $type & TYPE_VALIDATE_ADDRESS )
{
// validate input as address
}
if( $type & TYPE_VALIDATE_COUNTRY )
{
// validate input as country
}
// If its valid or is now santised
// insert into the $data variable
// if( $valid ) {
$this->data[ $name ] = $value;
// }
// data[ name ] now contains a safe value that can be accessed
}
public function get( $name )
{
// do checking to ensure it exists
// then return it
return $this->data[ $name ];
}
}
?>