1

我目前正在改进我自己的 MVC,但我无法为以下场景找到一个好的解决方案:从控制器(我基本上告诉模型如何处理输入)到各种模型。目前,我将每个用户输入都放入一个属性中:

    foreach($this->properties as $property => $empty)
    {
        if(isset($_POST[$property]))
        {
            $this->properties[$property] = htmlspecialchars(trim($_POST[$property]));
        }
    }

最终,当我需要一个新模型来做某事时,我会这样称呼它:

    new view('calendar',$data,$this->properties);

最后在模型中,我通过将输入/变量放入模型属性中来接收它们……</p>

class validation
{   

    public function __construct($values)
    {
        foreach($values as $property => $value)
        {
            $this->{$property} = $value;
        }
    }
}

这样我就不必考虑变量的来源(在验证用户输入之后,我不再关心)并且总是可以为我写一个干净的阅读版本$this->user_input

但不知何故,我觉得这不是最简单的方法,也可能不是一个好方法。最困扰我的是,在编写新的类/模型时,我总是要告诉模型将输入带入自己的属性中,并且在调用新类时总是要传递参数。

有没有什么方法可以让我在调用新类时从用户那里继承这些变量,而无需让控制器成为父类 - 或者让控制器成为父类实际上是否有意义?我认为当另一个控制器使用该模型时会令人困惑。

4

5 回答 5

1

好的,这里要记住的重要一点是,您的控制器“有一个”变量容器(保存所有属性),而不是控制器是(或“”)一个变量容器。所以首先,你应该使用组合,而不是继承。

对下面的以下片段进行了注释以更详细地解释。一些注意事项:

  • InputData 实例可以在控制器级别之上创建(例如,在不同的控制器中,以便可以在许多控制器之间共享),这回答了您问题的主要部分。关键是你只写了一次,你可以放心地说,一旦它在那里,就可以了

  • 您可以在 InputData 中包含所有验证方法,因为 InputData 的作用是安全地容纳/存储数据 - 在我看来这是一个很好的抽象级别,或者换句话说“它负责输入数据,如果出现问题输入数据,我知道去哪里看”

  • 最后,为了增加一点亮点,我添加了一些位操作,因此当通过 input_data->add 添加值时,它们可以针对多种类型进行验证(例如,可以添加一些需要验证为数字和帖子的内容代码)。

索引.php

require_once( "Controller.php" );

$controller = new Controller();
$controller->print_current_user();

控制器.php

<?
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 ];
    }
}
?>
于 2013-01-01T12:45:41.150 回答
1

根据您的需要,将用户输入存储在单例类(例如 UserRequest 类)的属性中可能是有意义的:

class UserRequest extends Singleton
{
    protected $userProperties;
    public function getUserProperties()
    {
        return $this->userProperties;
    }
    ...other methods...
}

在您的引导程序或路由类中,当您捕获用户输入时,将它们保存在您的请求实例中,然后让所有控制器扩展一个读取此属性的基类:

class baseController
{
    protected $userProperties;
    public function __construct()
    {
        $this->userProperties = Request::getInstance()->getUserProperties();
    }
}

然后所有控制器都可以访问它,您只需捕获它一次。

于 2012-12-26T21:46:00.560 回答
1

最困扰我的是,在编写新的类/模型时,我总是要告诉模型将输入带入自己的属性中,并且在调用新类时总是要传递参数。

因此,假设您在这里有两个问题:

  1. 重复定义每个类定义的属性。
  2. 为每个类创建传递参数。

从最简单和最基本的意义上说,您无法同时规避两者。如果你不告诉类(至少以某种方式)它代表哪些属性,它就不会知道。第二点有点类似,如果数据没有设置到类,就不行了。

因此,由于在技术上根本不可能阻止这两个,问题是如何让它更舒适并减少重复 - 如果可能的话。

一种可行的方法是将所有这些对象都归为同一类型。我的意思是实际上这些只是一些改进的数组,不是吗?

因此,您可以自己创建一个基类,您可以从中扩展它,其中包含所有需要的代码,例如导入数组、定义属性。

因此,您只需编写一次代码并根据需要创建尽可能多的对象和不同的“类型”。

举个例子,让我们创建一个这样的对象,它有一个可以完成工作的基类:

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";

这确实会从中导入一些$_POST与对象属性匹配的数据(类似于您所拥有的)。但是输出是一样的:

<h1>test</h1> let's throw some HTML in

你现在可能想要那个。因此,您可以创建一些装饰器,例如,这里有一个与回调函数一起使用的装饰器:

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";

现在这一次的输出是:

&lt;h1&gt;test&lt;/h1&gt; let's throw some HTML in

希望这会有所帮助。你可以在这里找到代码:Demo

于 2012-12-28T13:55:42.130 回答
1

我认为更好的解决方案是将所有输入存储在一个对象中,让我们调用它data。每个模型都可以有一个data属性。控制器完成输入验证后,您可以将对象传递给第一个模型并将其存储在那里。此时,您可以自由地将对象从模型传递到模型。如果您要更改其中的值,data您应该稍后使用方法调用$this->data = $Model->GetData();或其他方法更新控制器对象。

使用 MVC 范式,让模型访问控制器的属性是不明智的。控制器基本上应该启动所有通信,即控制器将数据传递给对其进行操作的模型,然后控制器请求该数据并将其放入视图中。让控制器持有数据和模型直接在其上运行并不是一个好习惯。

于 2012-12-26T21:59:55.350 回答
-1

让控制器成为父母真的有意义吗?

是的,这可能正是我会做的。然后,您可以将protected用于要共享/继承的属性。

于 2012-12-22T17:41:47.300 回答