How do I include CMS data in a view with MVC content?

edited January 2012 in Modules
After the Xmas break I've come back to a Fuel CMS project and find myself perplexed. On an earlier project in November, I managed without much thought to include admin inputted CMS content into a view that used both a controller and model. However, on the new project, I cannot find how to repeat this, and now realise I didn't completely understand the method before.

The working example goes something like this:

In the controller, I use:
$data = array(); ... form validation code, $data additions ... $page_init = array('location' => 'purchase_forms/newform'); $this->load->module_library(FUEL_FOLDER, 'fuel_page', $page_init); $this->fuel_page->add_variables($data); $this->fuel_page->render();

and in the newform.php view (amongst other things) I have:
<?php echo fuel_var('body', ''); ?> <form> ... </form>

The admin fields for the page "new form" have the "location" as "purchase_forms/newform" and uses the main.php layout. And it works just as I would expect - CMS content appears above the form. But I can't seem to replicate this! Oddly, the main.php layout file is this:

<?php $this->load->view('_blocks/header')?> <div id="main_inner"> <?php echo fuel_var('body', ''); ?> </div> <?php $this->load->view('_blocks/footer')?>

So this would imply that fuel_var('body', '') is parsed twice?! If I remove the instance from the view, I lose the CMS content. I should mention that the $config['fuel_mode'] = 'auto'.

So, first question: should I be expecting CMS content to cascade into a controller->view effortlessly, providing the fuel_var('body') variable is present, and the URL of the controller->view matches the "location" field in the page's admin?

Second question: what precisely do I gain from using the fuel_page() method over load->view()? I know that view variables are included, and inline editing code, but what else? Will I only be able to render Fuel CMS content using fuel_page() ?

Third question: how does the view (whatever it contains) get rendered into the layout? Is it appended to, or does it replace, the fuel_var("body") content ?

Fourthly, under what conditions are pages imported into the admin, and how (are they appended to the "body" field of "fuel_page_variables")? Is it only when there is a view URL match, or a controller->view URL match?

Many thanks for any enlightenment!

Comments

  • edited January 2012
    I set up the Widget demo site to test a few things, and found that if there is an unpublished page with CMS content, and you create a view with a matching URL, and that view has a fuel_var('body') variable, then the view can include the "unpublished" content. You can see this in another browser that isn't logged in to the admin.This is at a tangent to my questions above, but still curious.

    I would have expected the view to display, but not to pull in unpublished content.
  • edited 2:29PM
    Let me try and answer your questions:

    1. Yes (assuming that you have fuel_mode set to "auto" which you do). To ensure that the cms view is displayed instead of a view with perhaps the same location, you can at the "render_mode" parameter:
    $page_init = array('location' => 'purchase_forms/newform', 'render_mode' => 'cms');
    2. Using fuel_page will first grab variables from the variables file and then will grab variables associated with the page (given the location), as well as apply any site variables to the page and as you mentioned, will add the inline editing code. Using load->view would can be done but would require you to replicate some of that functionality if you are wanting variables found in your _variables files or from the fuel_page_variables table.
    3. A static view is the value of the $body variable by default (that can be configured) and will be used by the layout associated with the page (if there is one). If no layout is provided, then it will just render the view file.
    4. If there is a view file that matches the location value, then it will trigger the import window. You can also manually import them by clicking the upload button.

    With regards to your last post about the unpublished content, it is currently set up to not check the page status in the fuel_page_variables table when grabbing the variables for a static view filer page, however, we may change this.
  • edited 2:29PM
    Thank you for your detailed reply, the 'render_mode' argument is certainly new to me! I will give that a try soon.

    With regard to the un-published content appearing in a view on the same path, it occurred to me that there is some value in it not showing - that is if you wanted to show CMS content as a rule, but could also rely on a "fallback" page if the content was removed. For example, you might have a "maintenance" notice as a static view. Of course, you could handle both situations with the CMS, but for a busy admin, it's easier to "un-publish" than write new copy! Besides, there are probably more ingenious examples where that behaviour might be useful.
  • edited January 2012
    OK, I've been playing around with a default install of Fuel, & I think the reason I became confused about the earlier project was because the CMS content was displayed regardless of the publication status when using the fuel_var('body','') variable in the view, and I hadn't tried changing the status. If I had, I would have realised that method was not satisfactory.

    I just want to be able to render CMS & controller content together, with publishing status respected. If the CMS page is unpublished, the controller->view data appears by itself, but if the CMS page is published, then both appear.

    I think there must be a way of doing this, either by a) grabbing the CMS data in the controller and adding it to the variables fuel_page() uses, or, b) creating a fuel_model() grab of the page content, respecting the published status, in the view.

    Which would be the better solution, in your opinion?
  • edited January 2012
    Following on from the last post, for the a) option, I was thinking of something like this in the controller:

    $CI =& get_instance(); $CI->load->module_library(FUEL_FOLDER, 'fuel_page', array('location' => 'contact', 'render_mode' => 'cms')); $vars['thispage'] = $CI->fuel_page->variables('body'); ... $this->fuel_page->add_variables($vars); ...

    I can use the $thispage variable fine (here it fetches the CMS content for the contact page). However, I need to filter the returned result WHERE published = 'yes' (or equivalent). Is this possible?

    Or, at a higher level, would it be possible to add to the render_mode so:

    render_mode = 'both';

    where view & CMS are combined?
  • edited 2:29PM
    I've reached a solution - of sorts.

    I've written a helper to fetch the fuel_page_variables 'body' content where the fuel_pages 'location' value matches the current url, respecting the 'published' value. So I can use this in controllers that would otherwise not have CMS content passed on to them, confident that it won't show unpublished data.

    I'm not entirely happy with this, partly because I'm not sure if the helper should return an object or associative array (it does the latter for now), partly because it is in MY_helper (and feels like it could begin a 'page_helper') and partly because the 'location' field in fuel_pages is not unique, so matching a row on that could be dubious (if in practice OK). And of course, it must also be reproducing some of what fuel_page already does.

    So it would still be great if Fuel CMS allowed the option to do CMS only, view only, and both (respecting published status)!!
  • edited January 2012
    I think this it is possible to respect the page published status by adding the following where clause in fuel/modules/fuel/models/pagevariables_model.php in the _common_query method:
    function _common_query() { $this->db->join($this->_tables['pages'], $this->_tables['pages'].'.id = '.$this->_tables['pagevars'].'.page_id', 'left'); if (!defined('FUEL_ADMIN')) { $this->db->where(array($this->_tables['pages'].'.published' => 'yes')); }
  • edited January 2012
    That's a lot neater! I've tried it on a fresh install, and it seems to cover exactly what I was after. Thanks - I'm loathe to edit the system side stuff, so I didn't venture into that model. But in trying to get around it, I've learned a lot more about the CMS!

    Would that solution be added to Fuel in the future?

    I really appreciate your help - thanks for your patience.

    Do you imagine there will a book on Fuel CMS at any time - maybe like the Codeigniter ones from Wrox or Packt?
  • edited January 2012
    I found there is another quirk to what I'm trying to achieve! I should have worked this out earlier!

    If the view file is the same name as the controller and in the root of application/views, then fuel_var('body') delivers the content as you proposed (respecting published status, after the filter addition). Hurrah!

    On my current project, I have been tidily adding views to subfolders - eg events views go in an /events folder etc. But... fuel_var('body') is of course empty in this scenario, since the relative paths of the view and the request aren't the same (?). Hurroo.

    If I'm not totally wrong about that, it looks like I would have to dis-organise my view folders (not very keen to do this). Maybe I could just leave the ancillary files in their subfolders, or, again, collect the CMS content in the controller and insert it into the view.

    I didn't appreciate that the view file argument ('location') to the fuel_page library was a literal match to the location field in fuel_page_variables (which must be the case). I anticipated that the fuel_page() process bound CMS data (according to the request path) with anything found in the view (on whatever arbitrary path that might be), but that doesn't seem to be so.

    I'm wondering now if it's more coherent to create "advanced modules" for the stuff I'm doing (even if they don't perhaps seem that advanced).
  • edited 2:29PM
    I'm sorry but I'm not quite sure I follow the problem you are having. If it helps, you can always pass the 'view' variable to specify which view file you want to use:
    $this->fuel_page->add_vars(array('view' => 'events'));

    For a list of special variables you can pass to the page, look at the bottom of this page:
    http://www.getfuelcms.com/user_guide/general/opt-in-controllers
  • edited January 2012
    I'll give you a concrete example - but much simplified to save on space here!

    We create a page "foo" in the admin with some body content. And we have a controller (foo.php):
    <?php class Foo extends CI_Controller { function __construct() { parent::__construct(); } function index() { $vars['mvcdata'] = '<h2>Could be data from a model</h2>'; $page_init = array('location' => 'foo'); $this->load->module_library(FUEL_FOLDER, 'fuel_page', $page_init); $this->fuel_page->add_variables($vars); $this->fuel_page->render(); } }

    This has a view of the same name "views/foo.php":
    <?php echo fuel_var('body','No CMS here'); ?> <hr /> <?php echo $mvcdata; ?>

    This works fine - we see the content (CMS & MVC) as expected at url /foo. Now that we have also implemented the filter on the pagevariables common query, as per your suggestion, the CMS content will not show if the page data is "un-published". That's great, and I was very pleased with that solution indeed!

    However, I like to organise views into neat subfolders. If I change the controller to this:
    <?php class Foo extends CI_Controller { function __construct() { parent::__construct(); } function index() { $vars['mvcdata'] = '<h2>Could be data from a model</h2>'; $page_init = array('location' => 'foo/index'); $this->load->module_library(FUEL_FOLDER, 'fuel_page', $page_init); $this->fuel_page->add_variables($vars); $this->fuel_page->render(); } }

    and copy the same view code as before into "views/foo/index.php", the CMS content will never show (fuel_var() will always show the default 'No CMS here' string instead).

    I've tested this on the latest Fuel code, and think this is objective (although I'd like it not to be!) Maybe I have the wrong end of the stick, but I would expect the requested url (/foo) to trigger the CMS page content associated with it via the controller, regardless of the path to the view. Maybe I'm working against the 'opt-in controller' methodology here?
  • edited 2:29PM
    Regarding the line added to the page variables common query method, it has drastic repercussions on the admin behaviour - see http://www.getfuelcms.com/forums/discussion/comment/2582/#Comment_2582
  • edited 2:29PM
    Try this (note the view variable being passed):
    function index() { $vars['mvcdata'] = '<h2>Could be data from a model</h2>'; $vars['view'] = 'foo/index'; $page_init = array('location' => 'foo'); $this->load->module_library(FUEL_FOLDER, 'fuel_page', $page_init); $this->fuel_page->add_variables($vars); $this->fuel_page->render(); }
  • edited 2:29PM
    Yep - that works - along with the snippet you gave me for pagevariables model.

    I must have missed the "view" param to add_variables() in reading the guides. I didn't realise location and view could be defined individually, but it makes sense that they do. Now I've pored over the Fuel_page library code, I can see it was possible all along.

    Thanks again. You're very dedicated!
  • edited 2:29PM
    No problem. Glad you got it to work. At the bottom of this page is the list of other variables you can pass to your page that will do special things:
    http://www.getfuelcms.com/user_guide/general/opt-in-controllers
Sign In or Register to comment.