重新填充输入表单时,代码点火器控制器中的变量永远无法访问查看模板


variable in codeigniter controller never accessible to view template when re-populating input form

我已经用尽了所有的研究终点(文档,Google,SO等),所以我被迫公开问这个问题。我问题的本质应该由 CodeIgniter 3.0.6 官方文档在标题为"向视图添加动态数据"的部分中解决,因为我(认为)我正在处理一个变量,永远不会使我的控制器的范围被我的视图访问。

我正在向内容发布应用程序添加单个自定义修改,包括按 ID 获取内容的编辑页面以进行表数据更新。 第一,形式;

编辑。.PHP

<div class="row">
  <div class="col-md-12">
     <h3>Update post</h3>
         <form method="post" action="<?php echo base_url('post/update'); ?>" enctype="multipart/form-data">
     <div class="form-group">
       <label for="title">Title</label>
         <input type="text" name="title" id="title" placeholder="News title" value="<?php echo $data['post']->title; ?>" class="form-control" />
     </div>
     <div class="form-group">
       <label for="category">Category</label>
           <select name="category" id="category" class="form-control">
           <?php foreach($data['categories'] as $category): ?>
               <option value="<?php echo $category->idcategory; ?>" <?php echo set_select('category', $category->idcategory); ?>><?php echo $category->title; ?></option>
           <?php endforeach; ?>
           </select>
     </div>
     <div class="form-group">
       <label for="image">Image</label>
         <input type="file" name="image" id="image" class="form-control" placeholder="Upload an image" />
     </div>
     <div class="form-group">
       <label for="body">Post detail</label>
          <textarea name="body" id="body" class="form-control" placeholder="Provide news content. Basic HTML is allowed."><?php echo $data['post']->body; ?></textarea>
     </div>
     <div class="form-group">
       <label for="tags">Tags</label>
         <input type="text" name="tags" id="tags" value="<?php echo set_value('tags'); ?>" class="form-control" placeholder="Comma separated tags" />
     </div>
             <button type="submit" class="btn btn-primary">Submit</button>
    </form>
  </div>
</div>

我已经编辑了输入值以定位我需要在标题和帖子详细信息输入情况下填充表单的值(不需要更改类别输入的任何内容,因为它是一个出色的下拉列表),并保留标签表单输入,以便在我进行故障排除时不会破坏输出布局。控制器/函数 Post.php 是从原始的"添加"函数中欺骗的,我包括了整个而不是我觉得有问题的代码块;

更新功能

   public function update($idpost) {
    $this->load->helper('form');
    $data['title'] = 'Update post | News Portal';
    $data['post'] = $this->posts->get($idpost);
    $this->load->model('category_model', 'cm');
    $data['categories'] = $this->cm->get_all();
    $this->load->library('form_validation');
    $this->form_validation->set_rules('title', 'title', 'trim|required');
    $this->form_validation->set_rules('body', 'post body', 'trim|required');
    $this->form_validation->set_rules('tags', 'tags', 'required');
    if($this->input->method(TRUE) == 'POST' && $this->form_validation->run()) {
        $config['upload_path'] = './assets/uploads/';
        $config['allowed_types'] = 'gif|jpg|png';
        $config['max_size'] = '2000';
        $config['max_width']  = '2000';
        $config['max_height']  = '1200';
        $config['encrypt_name']  = TRUE;
        $this->load->library('upload', $config);
        if (!$this->upload->do_upload('image')) {
            $this->template->alert(
                $this->upload->display_errors(),
                'danger'
            );
        } else {
            $upload_data = $this->upload->data();
            $idpost = $this->posts->add(array(
                'iduser' => $this->user->id(),
                'title' => $this->input->post('title'),
                'body' => $this->input->post('body'),
                'image' => $upload_data['file_name']
            ));
            $tags = $this->input->post('tags');
            if(strlen($tags) > 0) {
                $this->load->model('tag_model', 'tm');
                $tags = explode(',', trim($tags));
                $tags = array_map(array($this->tm, 'set_tag'), $tags);
                $this->load->model('post_tag_model', 'ptm');
                foreach($tags as $idtag) {
                    $this->ptm->add(array(
                        'idpost' => $idpost,
                        'idtag' => $idtag
                    ));
                }
            }
            $idcategory = $this->input->post('category');
            if($idcategory) {
                $this->load->model('post_category_model', 'pcm');
                $this->pcm->add(array(
                    'idpost' => $idpost,
                    'idcategory' => $idcategory
                ));
            }
            $this->template->alert(
                'Updated news item successfully',
                'success'
            );
            redirect('post');
            return;
        }
    }
    $this->template->view('post/edit', $data);
}    

这个变量($tags)在控制器和视图之间以某种方式丢失,通过使用var_dump($this->_ci_cached_vars)来确认;检查相应视图中的所有可用对象。我只需要变量$tags来重新填充适合表单输入的数据。为什么这个函数中没有$tags的初始化?

完全理解变量永远不会传递给相应的视图,因为它不存在(正如我之前的var_dump所证实的那样),但我迷茫于如何在函数范围内准确地绘制$tags以便它可以帮助形成输入以检索目标数据?所有其他输入将根据需要重新填充。而且,顺便说一句,我实际上找到了这个项目的原始开发人员,并与他进行了讨论。我试图理清他概述的两种方法,但最终我得到了空白或错误的页面。理论上我最接近他的第二点 - 部分改编一些已经在新闻视图中的代码;

<?php
    if($tags = get_tags($data['news']->idpost)) {
        echo '<div class="tags">';
        echo 'Terms: ';
        foreach($tags as $tag) {
            echo ' <i class="fa fa-fw fa-link"></i> <a href="' . base_url('news/tag/' . $tag->idtag) . '">' . $tag->title . '</a> ';
        }
        echo '</div>';
    }
    ?>

它总是以未定义的索引/变量或其他一些的错误消息结尾,我试图打破我的脖子来解决,但只是继续陷入泥潭(哈哈!这对我来说似乎很简单,是我问题的基础,但我一直在兜兜转转,兜兜转转,直到我喝醉了头晕目眩,比开始时多十倍。有人可以提供一点理解吗?

的意思是,我得到了一个连接,因为它应该像这里提供的答案一样简单@将变量从控制器传递到CodeIgniter中的视图。但是,唉,这不是...提前感谢您的任何澄清线索。

@DFriend - 我不想过多地改变结构;

a) 从中欺骗的代码正在工作,唯一目标是将数据从适当的表拉入该表单输入,

b) 我不想干扰当前功能或无意中打开另一个问题,并且,

c) 我正在尝试将正确的元素归零。

感谢您

抽出宝贵时间和回答,@DFriend。

我相信

你问题的原因是因为redirect电话。通过调用它,您实际上是将新的 URL 放入浏览器中。网站是无状态的。如果不使用会话或其他方式,每个 URL 请求都是唯一的。因此,通过召唤redirect,你正在从存在中抹去任何关于$tags的知识。

您可以通过将$tags推入$_SESSION数组,然后在post控制器中检查并检索它来解决此问题。

或者,如果post()位于同一控制器中,您可以简单地调用它而不是重定向。 post()必须修改为接受参数,或者$tags必须是控制器类的属性。

所以直接打电话发帖,而不是

  redirect('post');
  return;

这样做

 $this->post($tags);
 return;

然后定义 post 以接受可选参数

public function post($tags=null){
    //somewhere in here
    if(isset($tags)){ 
        //$data is sent to views
        $data['tags'] = $tags;
    }
}

扩展答案:如何在 Codeigniter 中实现 Post/Read/Get 模式,并仍然使用字段验证和重新填充的表单字段。

使用 Codeigniter (CI) 实现用于处理的 PRG 模式需要扩展 CI_Form_validation 类。下面的代码应该是/application/libraries/MY_Form_validation.php

<?php
/**
 * The base class (CI_Form_validation) has a protected property - _field_data
 * which holds all the information provided by validation->set_rules() 
 * and all the results gathered by validation->run()
 * MY_Form_validation provides a public 'setter' and 'getter' for that property.
 *
 */
class MY_Form_validation extends CI_Form_validation{
    public function __construct($rules = array())
    {
        parent::__construct($rules);
    }
    //Getter
    public function get_field_data() {
      return $this->_field_data;    
}
    //Setter
    public function set_field_data($param=array()){
      $this->_field_data = $param;
    }
}

没有您使用的模板库,我回到了$this->load->view().在无法访问您的模型和相关数据的情况下,我不得不做出一些假设,在某些情况下,会留下数据库调用。

总的来说,我尽量不对结构进行太多改变。但我也想演示 form 帮助程序函数的几个方便用法。

在大多数情况下,我所做的重组主要是试图提供一个明确的例子。如果我成功地展示了这些概念,你应该能够相当容易地实现这一点。

这是修改后的观点。它更多地利用了"表单"帮助程序函数。

<head>
  <style>
    .errmsg {
      color: #FF0000;
      font-size: .8em;
      height: .8em;
    }
  </style>
</head>
<html>
  <body>
    <div class="row">
      <div class="col-md-12">
        <h3>Update post</h3>
        <?php
        echo form_open_multipart('posts/process_posting');
        echo form_hidden('idpost', $idpost);
        ?>
        <div class="form-group">
          <label for="title">Title</label>
          <input type="text" name="title" id="title" placeholder="News title" value="<?php echo $title; ?>" class="form-control" />
          <span class="errmsg"><?php echo form_error('title'); ?>&nbsp;</span>
        </div>
        <div class="form-group">
          <label for="category">Category</label>
          <?php
          echo form_dropdown('category', $categories, $selected_category, ['class' => 'form-control']);
          ?>
        </div>
        <div class="form-group">
          <label for="image">Image</label>
          <input type="file" name="image" id="image" class="form-control" placeholder="Upload an image" />
        </div>
        <div class="form-group">
          <label for="body">Post detail</label>
          <textarea name="body" id="body" class="form-control" placeholder="Provide news content. Basic HTML is allowed."><?php echo $body; ?></textarea>
          <span class="errmsg"><?php echo form_error('body'); ?>&nbsp;</span>
        </div>
        <div class="form-group">
          <label for="tags">Tags</label>
          <input type="text" name="tags" id="tags" value="<?php echo $tags ?>" class="form-control" placeholder="Comma separated tags" />
          <span class="errmsg"><?php echo form_error('tags'); ?>&nbsp;</span>
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
        <?= form_close(); ?>
      </div>
    </div>
  </body>
</html>

此解决方案的关键是在验证失败时将form_validation->_field_data存储在会话中。视图加载函数在会话数据中查找失败标志,如果该标志为 true,则form_validation->_field_data还原到当前form_validation实例。

我试图在评论中做很多解释。控制器中的以下内容包括显示和处理方法。

class Posts extends CI_Controller
{
  function __construct()
  {
    parent::__construct();
    $this->load->library('session');
    $this->load->library('form_validation', NULL, 'fv');
  }
  /**
   * post()
   * In this example the function that shows the posting edit page
   * Note the use of the optional argument with a NULL default
   */
  function post($idpost = NULL)
  {
    $this->load->model('category_model', 'cm');
    $categories = $this->cm->get_all();
    /*
     * Your model is returning an array of objects. 
     * This example is better served by an array of arrays. 
     * Why? So the helper function form_dropdown() can be used in the view.
     * 
     * Rather than suggest changing the model
     * these lines make the conversion to an array of arrays.
     */
    $list = [];
    foreach($categories as $category)
    {
      $list[$category->idcategory] = $category->title;
    }
    $data['categories'] = $list; // $data is used exclusivly to pass vars to the view

    if(!empty($idpost))
    {
      //if argument is passed, a database record is retrieved.
      $posting = $this->posts->get($idpost);

      //Really should test for a valid return from model before using it. 
      //Skipping that for this example
      $title = $posting->title;
      $body = $posting->body;
      //assuming your model returns the next two items like my made up model does
      $selected_category = $posting->category; 
      $tags= $posting->tags; 
    }
    else
    //a failed validation (or a brand new post)
    {
      //check for failed validation
      if($this->session->failed_validation)
      {
        // Validation failed. Restore validation results from session.
        $this->fv->set_field_data($_SESSION['validated_fields']);
      }
    }
    //setup $data for the view
    /* The 'idpost' field was add to demonstrate how hidden data can be pasted to and from
     * a processing method. In this case it would be useful in providing a 'where = $value'
     * clause on a database update. 
     * Also, a lack of any value could be used indicate an insert is requried for a new record.
     * 
     * Notice the ternary used to provide a default value to set_value()
     */
    $data['idpost'] = $this->fv->set_value('idpost', isset($idpost) ? $idpost : NULL);
    $data['title'] = $this->fv->set_value('title', isset($title) ? $title : NULL);
    $data['body'] = $this->fv->set_value('body', isset($body) ? $body : NULL);
    $data['tags'] = $this->fv->set_value('tags', isset($tags) ? $tags : NULL);
    $data['selected_category'] = $this->fv->set_value('category', isset($selected_category) ? $selected_category : '1');
    $this->load->view('post_view', $data);
  }
  public function process_posting()
  {
    if($this->input->method() !== 'post')
    {
      //somebody tried to access this directly - bad user, bad!
      show_error('The action you have requested is not allowed.', 403);
      // return;  not needed because show_error() ends with call to exit
    }
    /*
     * Note: Unless there is a rule set for a field the 
     * form_validation->_field_data property won't have 
     * any knowledge of the field. 
     * In Addition, the $_POST array from the POST call to the this page
     * will be GONE when we redirect back to the view! 
     * So it won't be available to help repopulate the <form>
     * 
     * Rather than making a copy of $_POST in $_SESSION we will rely
     * completely on form_validation->_field_data 
     * to repopulate the form controls.
     * That can only work if there is a rule set 
     * for ANY FIELD you want to repopulate after failed validation.
     */
    $this->fv->set_rules('idpost', 'idpost', 'trim');  //added any rule so it will repopulate
    // in this example required would not be useful for 'idpost'
    $this->fv->set_rules('title', 'title', 'trim|required');
    $this->fv->set_rules('body', 'post body', 'trim|required');
    $this->fv->set_rules('tags', 'tags', 'required');
    //add rule for category so it can be repopulated correctly if validation fails
    $this->fv->set_rules('category', 'category', 'required');
    if(!$this->fv->run())
    {
      // Validation failed. Make note in session data
      $this->session->set_flashdata('failed_validation', TRUE);
      //capture and save the validation results
      $this->session->set_flashdata('validated_fields', $this->fv->get_field_data());
      //back to 'posts/index' with server code 303 as per PRG pattern
      redirect('posts/post', 'location', 303);
      return;
    }
    // Fields are validated
    // Do the image upload and other data storage, set messges, etc
    // checking for $this->input->idpost could be used in this block 
    // to determine whether to call db->insert or db->update
    // 
    // When process is finished, GET the page appropriate after successful <form> post
    redirect('controller/method_that_runs_on_success', 'location', 303);
  }
//end Class
}

问题?评论?侮辱?