【问题标题】:Codeigniter blog application bug: form is loaded without validation errorsCodeigniter 博客应用程序错误:加载表单时没有验证错误
【发布时间】:2019-01-05 19:27:31
【问题描述】:

我正在使用 Codeigniter 3.1.8 和 Bootstrap 4 开发一个博客应用程序。我有一个带有验证的“编辑帖子”表单。

如果验证失败(例如,因为 Title 字段已被清空),表单应该重新加载验证错误

我的update()(位于 Posts 控制器中)方法是错误的:它使用重定向,所以表单被重新加载没有验证错误,到它的初始状态。

public function edit($id) {
    // Only logged in users can edit posts
    if (!$this->session->userdata('is_logged_in')) {
        redirect('login');
    }

    $data = $this->Static_model->get_static_data();
    $data['pages'] = $this->Pages_model->get_pages();
    $data['categories'] = $this->Categories_model->get_categories();
    $data['posts'] = $this->Posts_model->sidebar_posts($limit=5, $offset=0);
    $data['post'] = $this->Posts_model->get_post($id);
    if ($this->session->userdata('user_id') == $data['post']->author_id) {
        $data['tagline'] = 'Edit the post "' . $data['post']->title . '"';
        $this->load->view('partials/header', $data);
        $this->load->view('edit-post');
        $this->load->view('partials/footer');
    } else {
        /* If the current user is not the author
        of the post do not alow edit */
        redirect('/' . $id);
    }
}

public function update() {
    // Form data validation rules
    $this->form_validation->set_rules('title', 'Title', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_rules('desc', 'Short description', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_rules('body', 'Body', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_error_delimiters('<p class="error-message">', '</p>');

    $id = $this->input->post('id');

    // Update slug (from title)
    if (!empty($this->input->post('title'))) {
        $slug = url_title($this->input->post('title'), 'dash', TRUE);
        $slugcount = $this->Posts_model->slug_count($slug);
        if ($slugcount > 0) {
            $slug = $slug."-".$slugcount;
        }
    } else {
        $slug = $this->input->post('slug');
    }

// Upload image
    $config['upload_path'] = './assets/img/posts';
    $config['allowed_types'] = 'jpg|png';
    $config['max_size'] = '2048';

    $this->load->library('upload', $config);

    if(!$this->upload->do_upload()){
        $errors = array('error' => $this->upload->display_errors());
        $post_image = $this->input->post('postimage');
    } else {
        $data = array('upload_data' => $this->upload->data());
        $post_image = $_FILES['userfile']['name'];
    }

    if ($this->form_validation->run()) {
        $this->Posts_model->update_post($id, $post_image, $slug);
        $this->session->set_flashdata('post_updated', 'Your post has been updated');
        redirect('/' . $slug);
    } else {
        redirect('/posts/edit/' . $slug);
    }
}

我几乎可以肯定问题出在这一行:redirect('/posts/edit/' . $slug);,但我还没有找到可行的替代方案。

使用$this-&gt;edit($id) 代替redirect('/posts/edit/' . $slug); 也不起作用。我希望它会,因为我想让代码保持干燥。

我应该改变什么?


编辑。我这样做了:

if ($this->form_validation->run()) {
        $this->Posts_model->update_post($id, $post_image, $slug);
        $this->session->set_flashdata('post_updated', 'Your post has been updated');
        redirect('/' . $slug);
} else {
        $this->form_validation->run();
        $this->session->set_flashdata('errors', validation_errors());
        var_dump($this->session->flashdata('errors'));
        //redirect('/posts/edit/' . $slug);
}

var_dump($this-&gt;session-&gt;flashdata('errors')); 返回所有验证错误。

我希望将 has-error 类添加到表单组并附加 p class="error-message">The Title field is required.</p>。

<div class="form-group has-error">
  <input type="text" name="title" id="title" class="form-control error" placeholder="Title" data-rule-required="true" value="Learn to code with us" aria-invalid="true">
  <p class="error-message">The Title field is required.</p>
</div>

【问题讨论】:

  • 在函数中加载视图而不是重定向,或者您可以将错误放在会话中以在重定向后显示
  • 我刚刚注意到您没有在您的编辑方法中检查用户是否有权在您的更新方法中编辑帖子(您永远不应该假设用户不会尝试直接发布到不同的帖子 ID),因此最好在两种方法中添加相同的检查。
  • @ahmad 请发布答案。谢谢!

标签: php codeigniter validation


【解决方案1】:

您有 3 个选项:

  1. 失败时在更新方法中使用闪存数据(成功时您已经在使用它)。只需将错误分配给闪存数据变量,然后在重定向回编辑后获取它。
  2. 结合编辑和更新方法(最常见于非 ajax 使用)。
  3. 使用 ajax 并返回 json 编码的字符串以获取错误或成功消息。

选项 2:

此选项还解决了 cmets 中指出的潜在身份验证问题。

请阅读代码中嵌入的 cmets。

public function edit($id) {
        // Only logged in users can edit posts
        if (!$this->session->userdata('is_logged_in')) {
            redirect('login');
        }

        $data['post'] = $this->Posts_model->get_post($id);

        if ($this->session->userdata('user_id') == $data['post']->author_id) {
            show_error('Access denied'); // function exits
        }

        if ($_POST) {

            $this->form_validation->set_rules('title', 'Title', 'required', array('required' => 'The %s field can not be empty'));
            $this->form_validation->set_rules('desc', 'Short description', 'required', array('required' => 'The %s field can not be empty'));
            $this->form_validation->set_rules('body', 'Body', 'required', array('required' => 'The %s field can not be empty'));
            $this->form_validation->set_error_delimiters('<p class="error-message">', '</p>');

            //$id = $this->input->post('id'); not required anymore

            $config['upload_path'] = './assets/img/posts';
            $config['allowed_types'] = 'jpg|png';
            $config['max_size'] = '2048';

            $this->load->library('upload', $config);

            if ($this->form_validation->run() && $this->upload->do_upload()) {
                // always use the name from the upload lib
                // sometimes it changes it in case of duplicates (read docs for more)
                $post_image = $this->upload->data('file_name');

                // doesn't make sense with title validation rule, this will always be true to get
                // passed validation
                if (!empty($this->input->post('title'))) {
                    $slug = url_title($this->input->post('title'), 'dash', TRUE);
                    $slugcount = $this->Posts_model->slug_count($slug);
                    if ($slugcount > 0) {
                        $slug = $slug . "-" . $slugcount;
                    }
                } else {
                    $slug = $this->input->post('slug');
                }

                $this->Posts_model->update_post($id, $post_image, $slug);
                $this->session->set_flashdata('post_updated', 'Your post has been updated');
                redirect('/' . $slug);
            } else {
                $data['errors'] = validation_errors() . $this->upload->display_errors();
            }
        }

        $data = $this->Static_model->get_static_data();
        $data['pages'] = $this->Pages_model->get_pages();
        $data['categories'] = $this->Categories_model->get_categories();
        $data['posts'] = $this->Posts_model->sidebar_posts($limit = 5, $offset = 0);
        $data['tagline'] = 'Edit the post "' . $data['post']->title . '"';
        $this->load->view('partials/header', $data);
        $this->load->view('edit-post');
        $this->load->view('partials/footer');
    }

【讨论】:

  • 我不想要闪信,我只想返回无效的表单。我可以在前端进行此验证并且我,但我需要后端验证以防(不太可能)用户禁用 JavaScript。
【解决方案2】:

在这种情况下我要做的是将两种方法合并为一个方法,依靠验证来了解我是否保存条目。

您的代码将如下所示:

public function edit($id) {
    // Only logged in users can edit posts
    if (!$this->session->userdata('is_logged_in')) {
        redirect('login');
    }
    $Post = $this->Posts_model->get_post($id);
    // user does not own the post, redirect
    if ($this->session->userdata('user_id') !== $Post->author_id) {
        redirect('/' . $id);
    }

    // Form data validation rules
    $this->form_validation->set_rules('title', 'Title', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_rules('desc', 'Short description', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_rules('body', 'Body', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_error_delimiters('<p class="error-message">', '</p>');

    // if validation fails, or the form isn't submitted
    if ($this->form_validation->run() === false ) {
        $data = $this->Static_model->get_static_data();
        $data['pages'] = $this->Pages_model->get_pages();
        $data['categories'] = $this->Categories_model->get_categories();
        $data['posts'] = $this->Posts_model->sidebar_posts($limit=5, $offset=0);
        $data['post'] = $Post;

        $data['tagline'] = 'Edit the post "' . $data['post']->title . '"';
        $this->load->view('partials/header', $data);
        $this->load->view('edit-post');
        $this->load->view('partials/footer');
    }else{
        // Update slug (from title)
        if (! empty($this->input->post('title'))) {
            $slug = url_title($this->input->post('title'), 'dash', TRUE);
            $slugcount = $this->Posts_model->slug_count($slug);
            if ($slugcount > 0) {
                $slug = $slug."-".$slugcount;
            }
        } else {
            $slug = $this->input->post('slug');
        }
        // Upload image
        $config['upload_path'] = './assets/img/posts';
        $config['allowed_types'] = 'jpg|png';
        $config['max_size'] = '2048';

        $this->load->library('upload', $config);

        if(!$this->upload->do_upload()){
            $errors = array('error' => $this->upload->display_errors());
            $post_image = $this->input->post('postimage');
        } else {
            $data = array('upload_data' => $this->upload->data());
            $post_image = $_FILES['userfile']['name'];
        }       
        $this->Posts_model->update_post($id, $post_image, $slug);
        $this->session->set_flashdata('post_updated', 'Your post has been updated');
        redirect('/' . $slug);
    }
}

并且您不必单独实施更新,您只需发布到 /edit/$id 而不是 /update/$id ...这是一个粗略的示例,您的 slug 检查(我没有触及on) 不是正确的方法,如果它通过验证,则标题已经填写,因为它被设置为必需,所以我猜你的意思是if (! empty(slug) ),但同样在你的 else 中,你直接从用户输入设置 slug,所以我会将它添加到验证中,并确保它在数据库中是唯一的,除了当前正在编辑的 $id。

同样,这是从您的原始代码复制和粘贴的结果,我可能遗漏了一些内容,因此请仔细阅读以确保没有遗漏任何内容,并在您传递给视图的数据中包含验证错误。

【讨论】:

  • 希望能够使用 $this-&gt;edit($id) 而不是 redirect('/posts/edit/' . $slug); 之类的东西来使我的代码尽可能干燥。
  • 是的,这当然是一个选择……你试过了吗?
  • 我做了,但由于某种原因它不起作用。我可以在这里粘贴整个应用程序。所以,这里是 Github。看看吧。
  • 您遇到什么错误?我认为它应该可以正常工作,你有 $id 并且它在同一个类中可用。
  • 我被重定向到siteurl.com/post_id 而不是siteurl.com/posts/edit/post_slug。我看到一条“抱歉,我们找不到您要查找的内容”的消息。
【解决方案3】:

按照@Alex 的建议,我已经设法使用set_flashdata() 获得了预期的结果

在我的控制器中:

public function update() {
    // Form data validation rules
    $this->form_validation->set_rules('title', 'Title', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_rules('desc', 'Short description', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_rules('body', 'Body', 'required',  array('required' => 'The %s field can not be empty'));
    $this->form_validation->set_error_delimiters('<p class="error-message">', '</p>');

    $id = $this->input->post('id');

    // Update slug (from title)
    if (!empty($this->input->post('title'))) {
        $slug = url_title($this->input->post('title'), 'dash', TRUE);
        $slugcount = $this->Posts_model->slug_count($slug);
        if ($slugcount > 0) {
            $slug = $slug."-".$slugcount;
        }
    } else {
        $slug = $this->input->post('slug');
    }

// Upload image
    $config['upload_path'] = './assets/img/posts';
    $config['allowed_types'] = 'jpg|png';
    $config['max_size'] = '2048';

    $this->load->library('upload', $config);

    if(!$this->upload->do_upload()){
        $errors = array('error' => $this->upload->display_errors());
        $post_image = $this->input->post('postimage');
    } else {
        $data = array('upload_data' => $this->upload->data());
        $post_image = $_FILES['userfile']['name'];
    }

    if ($this->form_validation->run()) {
        $this->Posts_model->update_post($id, $post_image, $slug);
        $this->session->set_flashdata('post_updated', 'Your post has been updated');
        redirect('/' . $slug);
    } else {
        $this->form_validation->run();
        $this->session->set_flashdata('errors', validation_errors());
        redirect('/posts/edit/' . $slug);
    }
}

在视图中:

<?php if ($this->session->flashdata('errors')) {
         $errors = $this->session->flashdata('errors');
         echo '<div class="error-group alert alert-warning alert-dismissible">' . "\n";
         echo '<button type="button" class="close" data-dismiss="alert">&times;</button>' . "\n";
         echo $errors;
         echo '<p class="error-message">We have restored the post.</p>';
         echo '</div>';
} ?>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-01
    • 1970-01-01
    • 2018-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多