Codeigniter:最佳的部分视图结构方式

40

您会如何在Codeigniter中构建下面这个页面的结构?

alt text

我考虑为每个部分创建单独的控制器:

  1. 左侧导航
  2. 内容导航
  3. 登录名
  4. 排行榜

不包括内容部分(因为这取决于左侧导航和内容导航上使用的链接,作为一种子菜单)。所有其他部分基本保持不变。

我考虑做如下处理:

Class User_Profile extends Controller
{

    function index()
    {
        $this->load_controller('Left_Nav');
        $this->load_controller('Content_Nav');
        $this->load_controller('Login_Name');
        $this->load_controller('Leaderboard', 'Board');

        $this->Left_Nav->index(array('highlight_selected_page' => 'blah'));

        $this->load('User');

        $content_data = $this->User->get_profile_details();

        $this->view->load('content', $content_data);

        $this->Login_Name->index();
        $this->Board->index();
    }

}

显然这个load_controller不存在,但是这个功能很有用。每个部分的控制器从模型中获取所需的数据,然后通过$this->view->load()加载页面。

在所有左侧导航链接(如新闻、用户、关于我们等)中都加入此代码可能会让人头疼。但是并非每个导航链接都有所有这些部分,因此我需要将这些部分作为“局部视图”的灵活性。

有人能提出更好的方法吗?

7个回答

29

对于CI 2.0以下版本,@Reinis的回答可能是正确的,但是很多东西已经发生了变化。因此,我想用最新的方法来回答这个问题。

大部分内容与@Reinis的方法相似,并且也在这里描述:http://codeigniter.com/wiki/MY_Controller_-_how_to_extend_the_CI_Controller

然而,这里是我所做的更新:

步骤1:创建一个MY_Controller.php文件并将其存储在/application/core目录中。

步骤2:在MY_Controller.php文件中放入以下内容:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class MY_Controller extends CI_Controller {

    function __construct()
    {
        parent::__construct();
    }

    function _output($content)
    {
        // Load the base template with output content available as $content
        $data['content'] = &$content;
        echo($this->load->view('base', $data, true));
    }

}

第三步:创建一个样例控制器,以MY_Controller.php为基础,本例中我将在application/controllers/下创建一个welcome.php控制器,并使用以下内容:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Welcome extends MY_Controller {

    function __construct()
    {
        parent::__construct();
    }

    public function index()
    {
        $this->load->view('welcome_message');
    }

}

一旦您设置好这些控制器,请执行以下操作:

步骤4:在/application/views内创建一个基础视图,并将文件命名为base.php,文件的内容应类似于以下内容:

<!DOCTYPE html>
<!--[if IE 7 ]><html lang="en" class="ie7"><![endif]-->
<!--[if IE 8 ]><html lang="en" class="ie8"><![endif]-->
<!--[if gt IE 8]><!--><html lang="en"><!--<![endif]-->
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <title></title> 
        <link rel="stylesheet" href="<?php echo base_url(); ?>stylesheets/reset.css" media="screen" />
    </head>
    <body>
        <div id="section_main">
            <div id="content">
                <?php echo $content; ?>
            </div>
        </div>
        <?php $this->load->view('shared/scripts.php'); ?>
        </div>
    </body>
</html>

第五步:在/application/views中创建另一个视图,并将此视图命名为welcome_message.php,该文件的内容为:

<h1>Welcome</h1>

一旦所有这些步骤完成,您应该看到以下输出:

<!DOCTYPE html>
<!--[if IE 7 ]><html lang="en" class="ie7"><![endif]-->
<!--[if IE 8 ]><html lang="en" class="ie8"><![endif]-->
<!--[if gt IE 8]><!--><html lang="en"><!--<![endif]-->
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <title></title> 
        <link rel="stylesheet" href="http://somedomain.local/stylesheets/reset.css" media="screen" />
    </head>
    <body>
        <!-- BEGIN: section_main -->
        <div id="section_main">
            <div id="content">
                <h1>Welcome</h1>
            </div>
        </div>
        <!-- END: section_main -->
        <script src="/path/to/js.js"></script>
        </div>
    </body>
</html>

正如您所看到的,<h1>欢迎</h1>被放置在基本模板中。

资源:

希望这有助于其他遇到这种技术的人。


3
如果您是从一个干净的安装开始进行操作,您还需要在application/config/autoload.php文件中启用"url"助手(以使base_url()函数正常工作)。 - Christian Davén
我理解你基本上是先构建初始内容视图,然后在输出之前构建基础视图并将预先构建的内容添加到基础视图中。我不理解按引用传递?我已经以相同的方式设置了这个,并且只向基础视图传递了一些更多的数据数组位(例如page_title、page_keywords等),但它给我显示一个空白屏幕...所以似乎没有加载基础视图,但也没有错误...查看$data确实显示了所有正确的信息,因此内容已经构建好了,也没有任何拼写错误。有什么想法吗? - mtpultz

26

我不能保证这是最佳方法,但我会创建一个基本控制器,就像这样:

class MY_Controller extends CI_Controller {

    public $title = '';
    // The template will use this to include default.css by default
    public $styles = array('default');

    function _output($content)
    {
        // Load the base template with output content available as $content
        $data['content'] = &$content;
        $this->load->view('base', $data);
    }

}

名为'base'的视图是一个模板(包含其他视图的视图):

<?php echo doctype(); ?>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <?php $this->load->view('meta'); ?>
    </head>
    <body>
        <div id="wrapper">
            <?php $this->load->view('header'); ?>

            <div id="content">
                <?php echo $content; ?>
            </div>

            <?php $this->load->view('footer'); ?>
        </div>
    </body>
</html>

这样做的目的是让每个控制器将其输出包裹在基本模板中,并且视图具有有效的HTML,而不是一个视图中打开标签并在另一个视图中关闭标签。如果我想让特定控制器使用不同或没有模板,我可以重写神奇的_output()方法。

一个实际的控制器可能看起来像这样:

class Home extends MY_Controller {

    // Override the title
    public $title = 'Home';

    function __construct()
    {
        // Append a stylesheet (home.css) to the defaults
        $this->styles[] = 'home';
    }

    function index()
    {
        // The output of this view will be wrapped in the base template
        $this->load->view('home');
    }
}

这样我就可以在视图中使用其属性,例如这样(这是填充<head>元素的“meta”视图):

echo "<title>{$this->title}</title>";
foreach ($this->styles as $url)
    echo link_tag("styles/$url.css");

我喜欢我的方法,因为它遵循DRY原则,并且在代码中只包含一次标题、页脚和其他元素。


这似乎是一个不错的选择,但我对“嵌入其他视图”的想法并不感兴趣——其他视图如何获取数据?我认为只有在包含的视图具有静态内容时才能很好地工作。 - Gary Green
视图只是包含文件,因此它们与基本模板共享相同的控制器($this)和变量。请看我如何在“meta”视图中使用$this->title,该视图由基本模板包含。我也可以在那里使用$content - slikts
1
在我绞尽脑汁理解 _output 是 Codeigniter 的一个神奇函数之后,它看起来是一种不错的方式。所以基本上,如果你想要一个新的模板/布局,你可以创建另一个 MY_New_Template 并扩展你的新控制器来使用它?我仍然不完全相信这是嵌入其他视图的好主意,因为你需要获取数据并将其传递到每个视图。我认为这就是控制器的作用,而视图不应该直接调用,流程应该始终是控制器 --> 获取数据 --> 传递给视图,或者我漏掉了什么? - Gary Green
视图共享相同的上下文,即基本模板将自动与其调用的视图共享数据。我不确定为什么它对你不起作用,但我已经检查过了,我认为我的示例代码是正确的。 - slikts
@GaryGreen:如果你在2.1.0核心中检查Output.php,它只会在你的派生控制器没有_output方法时输出内容。请检查core/output.php中的第406行。 - user799490
显示剩余2条评论

6
我的模板库可以处理所有这些。您可以创建一个或多个包含部分和主体内容标记的布局文件。
语法非常简单:
// Set the layout: defaults to "layout" in application/views/layout.php
$this->template->set_layout('whatever') 

// Load application/views/partials/viewname as a partial
$this->template->set_partial('partialname', 'partials/viewname');

// Call the main view: application/views/bodyviewname
$this->template->build('bodyviewname', $data); 

很简单,对吧?

将一些内容放入MY_Controller中,那就更容易了。


1

我喜欢Phil Sturgeon提到的内容。虽然它被认为非常复杂,但我真的很喜欢Magento拥有的模板结构。

受到这种结构方式的启发,我制定了我的逻辑(虽然不是很好,但尽可能简单。也许我可以覆盖->视图加载器并使其接受某种对象作为模板名称,然后根据需要加载结构)

第一:这种方法必须非常负责任地使用(您必须在控制器/方法中准备模板所需的数据!

第二:模板需要准备和正确结构化。

这就是我做的事情:

  • 在每个控制器中,我都有一个数组类型的属性,类似于这样:

    class Main extends CI_Controller {
    
    public $view = Array(
            'theend' => 'frontend',
            'layout' => '1column',
            'mainbar' => array('content','下一个模板文件加载在其下面'),
            'sidebar' => array('generic','下一个模板文件加载在其下面'),
            'content' => '',
    );
    
  • 对于每个我想要使用先前结构的方法,如果我想要稍微更改它,我会像这样编写它:

    public function index()
    {
    $data['view'] = $this->view;  // 我获取/加载全局类的属性
    $data['view']['mainbar'] = Array('archive','related_posts'); // 我更改了它的主栏部分
    // 我添加/加载需要在所有这些需要的模板中使用的数据 $data['my_required_data_that_i_use_in_template_files'] = 1;
    $this->load->view('main',$data); //
    }
    

第三步 在/application/view文件夹中,我有以下结构:

/view/main.php <-- which basically just determines which side's wrapper of web to load (frontend or backend or some other)

/view/frontend/wrapper.php

/view/backend/wrapper.php

/view/mobile/wrapper.php   <-- this wrappers are again another level of structuring for ex:

/view/backend/layouts/   <-- inside i have templates different layouts like 1column.php 2columns-left (have left side is narrow one),2columns-right,3columns... etc...

/view/backend/mainbar/   <-- inside i have templates for mainbar in pages

/view/backend/mainbar/.../ <-- in the same way it's possible to add folders for easily grouping templates for example for posts so you add for example

    /view/backend/mainbar/posts/  <-- all templates for creating, editing etc posts... 

    /view/backend/sidebar/   <-- inside i have templates for sidebar in pages

    /view/backend/...other special cases.... like dashboard.php

/app/view/main.php 中的 forth 文件看起来像这样:

if ($view['theend'] == "frontend")
{
$this->load->view('/frontend/wrapper');
} elseif ($view['theend'] == "backend")
{
$this->load->view('/backend/wrapper');
}

第五个包装器是一些 PHP 结构化 HTML 的简单应用,其中你有:

  • head(加载 HTML 标头、标题等...)
  • header/headers(如果传递的 $data['view']['headers'] 变量/数组中有任何头文件,则加载它们)
  • layout(加载布局文件,该文件只是具有下一级加载文件的新 HTML 结构化文件)
  • footer/footers(如果传递的 $data['view']['footers'] 变量中有任何页脚,则加载它们)
  • scripts(在标签之前加载脚本,如分析/ Facebook 脚本)

第六个同样,布局也会加载在 public $view = Array(...) 中指定的主栏/侧边栏内容。

如果我需要在某个方法中,我只需覆盖 public $view = Array(...) 属性的一部分,我只覆盖不同的部分。

它是这样完成的:

public function index()
{
    $data['view'] = $this->view;  // i take/load global class's attribute
    $data['view']['mainbar'] = Array('archive','related_posts'); // i change mainbar part of it
// i add/load data that i need in all those templates that are needed $data['view'] also my using same Array  $data['my_required_data_that_i_use_in_template_files'] = 1;
    $this->load->view('main',$data); //
}

最终的加载过程如下:

  1. $this->load->view('main',$data); <-- 加载/app/view/main.php并传递$data $data有一个名为'view'的节点($data['view']),其中包含其他重要信息,例如:结束位置、布局、标题、页脚等...

  2. 使用$data['view']['theend']中定义的数据来加载适当的包装器

  3. 再次使用$data['view']['layout']中的数据,在包装器中加载其他更深层次的结构,如布局...
  4. 布局使用相同的$data['view']['mainbar']、$data['view']['sidebar']和其他重要部分,如主栏模板、侧边栏模板等。

就是这样...

p.s. 很抱歉我没有使用数字,但stackoverflow系统非常奇怪,它不会显示3.而是显示1..正如您所看到的,我有一些嵌套列表...


1

你考虑过模板吗?只要稍微搜索一下,就会发现有很多不错的模板可用 - 可以查看CI维基。

模板几乎可以完全满足你的需求。你只需要定义一个主模板和“部分”,每次加载时这些部分都会为你加载。

我不想过多地推销,所以这里提供一个起点 - CI中的模板库


1

我会创建一个MY_Controller来处理所有这些事情。您可以使用布局(模板)/导航库来生成所有布局、导航、显示/突出显示选定的菜单项、加载视图等。

如果您为每个页面部分使用控制器,那么我认为这不是正确的方法。您可以使用视图和嵌套视图来实现。


-2
在 Kohana 2 中,我所做的是将所有子部分(如左侧菜单、顶部标题)放在一个模板中,并使用单个控制器填充将在模板中替换的变量。
然后,每个子部分的变量可以通过在控制器本身中调用的函数生成。您还可以将这些函数放在单独的控制器类的构造函数中,使您的每个控制器都扩展该控制器,以便它们自动运行并设置为类变量以便轻松访问。
对于稍微好看一些的模板,您可以将子部分放在单独的文件中,然后在大模板中包含它们:
<?php include 'leftMenu.php'; ?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接