I'm new to working with CI and I have a question. More like a design method suggestion. I want to create a base template for static pages. Basically I want to load doc type, head, and the body but nothing else. I want the content to be loaded by whatever class/function I call via the URL. I know I can do this with a HTML template and str_replace() but is there a better way or some fancy CI method I'm not familiar with?
Here is what I have so far and it works but it's not the best.
class Sandbox extends CI_Controller {
public function __construct() {
parent::__construct();
//echo "hello world?";
}
public function load($what){
if ($what == 'something'){
if (!file_exists('application/views/content/'.$what.'.php')){
// Whoops, we don't have a page for that!
show_404();
}
$data = array();
$this->load->view('templates/header', $data);
$this->load->view('content/'.$what, $data);
$this->load->view('templates/footer', $data);
}
else { // Default load
$data = array();
$data['message'] = 'Hello World';
//$this->load->view('templates/header', $data);
$this->load->view('content/sandbox', $data);
//$this->load->view('templates/footer', $data);
}
}
}
When I load the view method it seems to work but I had to add the doctype and head to the template. I would prefer to load those seperately so do not have to create multiple headers. What I am looking for is a way to specifically load parts of the page and whatever scripts/css that are required for that given page.
Thanks for any advice.
Your function names in the controller are references to the url eg:
www.ci.com/sandbox/load
The above url would load function load() inside the sandbox controller.
To create a template you can just create a view with the basics that you want and then pass the content to that view eg:
function home(){
$data['content'] = $this->load->view('home_content',,TRUE);
this->load->view('template', $data)
}
By using TRUE in the 3rd argument you are returning the data rather than displaying it.
Then in the template file just echo $content;
You could also create separate head files and other files to include in your template using the same method ie:
function home(){
$data['content'] = $this->load->view('home_content',,TRUE);
$data['head'] = $this->load->view('head',,TRUE);
this->load->view('template', $data);
}
You can also pass the data variable to views that you are returning ie:
$data['content'] = $this->load->view('home_content',,TRUE);
$data['admin_script'] = $this->load->view('admin_script',,TRUE);
$data['head'] = $this->load->view('head',$data,TRUE);
I hope this helps! :)
ALSO
If you want to load different pages you should create separate functions for them and create a new routing rule : Codeigniter: URI ROUTING
Related
I'm brand new to CodeIgniter, so apologies if I'm missing something obvious here.
I'm comfortable with sending data from a controller to a view file using return view('default/blog/index', $data);. My issue is accessing the same data in a layout file which is extended by the view file using <?= $this->extend('layouts/default'); ?>.
For example, if I insert <?= $data['content'] ?> in my view file, it displays as expected. If I insert the same code in the layout file that is extended by my view file, I get the "Trying to access array offset on value of type null" exception.
What am I missing that will allow me to access my data from within the layout file?
Thanks in advance.
Update:
So in my BlogController I've got
class BlogController extends BaseController
{
public function index()
{
$model = new Blog();
$data = $model->getBlog();
return view('default/blog/index', ['data' => $data]);
}
public function item($slug = null){
$model = new Blog();
$data = $model->getBlog($slug);
return view('default/blog/item', ['data' => $data]);
}
}
And then in my item.php and index.php files, I have
<?= $this->extend('layouts/default', ['data' => $data]); ?>
My Blog Model's getBlog() method:
public function getBlog($slug = false)
{
if ($slug === false) {
return $this->orderBy('bs_created_dt', 'desc')->findAll();
}
return $this->where(['bs_slug' => $slug])->first();
}
When I use the debug toolbar to inspect the data, it is showing as expected, and I can display it in the view files, but not in the layout file.
In Codeigniter, you need to pass data also in an extended file called layouts.
Because you want to access data inside the extended file and for that, you just need to pass data to that file.
So replace the line of code of extended view with this :
$this->extend('layouts/default', ['data' => $data]);
Figured this out - absolute rookie mistake.
I'm working off of a pre-existing template, and the previous developer had overwritten the $data variable in layout file before where I was trying to use it.
I'm off to stand in the corner for a while.
Just a quick question. Anyone know if its possible to load multiple views from codeigniter to mpdf controller and convert it to pdf?
my controller :
`
<?php
class C_Pdf extends CI_Controller {
private $params = array();
function __construct() {
parent::__construct();
$this->params['set_tag'] = array();
$this->params['set_css'] = array();
$this->params['set_js'] = array();
$this->params['title_auto'] = true;
$this->params['title'] = '';
}
public function index(){
//this data will be passed on to the view
$data['the_content']='mPDF and CodeIgniter are cool!';
$data['other']="siema heniu cos przeszlo";
//load the view, pass the variable and do not show it but "save" the output into $html variable
$this->load->view('common/default_header',$this->params);
$html=$this->load->view('reservation/complete', $data,true);
$this->load->view('common/default_footer');
//this the the PDF filename that user will get to download
//load mPDF library
$this->load->library('m_pdf');
//actually, you can pass mPDF parameter on this load() function
$pdf = $this->m_pdf->load();
//generate the PDF!
$pdf->WriteHTML($html);
//offer it to user via browser download! (The PDF won't be saved on your server HDD)
$pdf->Output();
}
}?>
i want to add footer and header from other view.
Just prefetch the header and footer templates into variables and pass their parsed strings to the main view ,like this:
$data['header'] = $this->load->view('common/default_header',$this->params, true);
$data['footer'] = $this->load->view('common/default_footer', true);
$html = $this->load->view('reservation/complete', $data, true);
Notice the third parameter to true so that you get the view in a string instead of sending it to output (most commonly the client's browser).
Then in your main template, place the $header and $footer variables wherever you need.
I'm using Cakephp with json parse extension and the RequestHandler component in order to create Web services using json.
I created a controller named Ws
In this controller I have a named userSubscribe
In order to avoid a lot of If else statements in the next methods, I thought about using a private function inside this controller that will check somes conditions and stop the script normaly BUT ALSO render the json normaly. I just want to do a DRY way !
My question is :
How could I render the json view in a sub function (called by the userSubscribe) ?
To make it clear, here is the style code that would like
public function userSubscribe() {
$this->check();
// Following code only executed if check didn't render the json view
// $data = ...
$code = 1;
$i = 2;
}
private function check() {
$input = &$this->request->data;
if ($_SERVER["CONTENT_TYPE"] != "application/json") { // For example
$result = "KO";
$this->set(compact("result"));
$this->set('_serialize', 'result');
$this->render(); // HERE, it will stop the 'normal behaviour' and render the json with _serialize
}
if (!isset($input["input"])) {
$result = "KO";
$this->set(compact("result"));
$this->set('_serialize', 'result');
$this->render(); // HERE, it will stop the 'normal behaviour' and render the json with _serialize
}
}
It's seems to be quite simple to do, but why can't I find the answer ?!
Thanks in advance for clue/advise/anything !
I have been successfully using XML view files in CakePHP (request the XML output type in headers so CakePHP will use e.g. Orders/xml/create.ctp instead of Order/create.ctp).
However, now i need to add some functionality that requires me to the reformat the XML at the end of most business logic in the controller.
So i tried this in the controller action:
public function createorder() {
$this->autoRender = false; // disable automatic content output
$view = new View($this, false); // setup a new view
{ ... all kinds of controller logic ...}
{ ... usually i would be done here and the XML would be outputted, but the autorender will stop that from happening ... }
{ ... now i want the XML in a string so i can manipulate the xml ... }
$view_output = $view->render('createorder'); // something like this
}
But what this gives me is:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<error>View file "/Users/test/Documents/hosts/mycakeapp/app/View/Orders/createorder.ctp" is missing.</error>
<name>MissingViewException</name>
<code>500</code>
<url>/orders/createorder/</url>
</response>
So i need to tell CakePHP to pickup the xml/createorder.ctp instead of createorder.ctp. How do i do this?
Cheers!
This answers refers to cakephp 2.4
I have been successfully using XML view files in CakePHP (request the XML output
type in headers so CakePHP will use e.g. Orders/xml/create.ctp
instead of Order/create.ctp).
In lib/Cake/View you can see different View files like:
View.php
XmlView.php //This extends View.php
JsonView.php //This extends View.php
So you told cakephp to use the XmlView. When you create a new View you need to use the XmlView instead of View. Or you can create your own custom View and put it inside app/View folder. In your custom View you can set your subdir.
<?php
App::uses('View', 'View');
class CustomView extends View {
public $subDir = 'xml';
public function __construct(Controller $controller = null) {
parent::__construct($controller);
}
public function render($view = null, $layout = null) {
return parent::render($view, $layout);
}
}
So what you need now is to create your custom view $view = new CustomView($this, false);
You can also write in your CustomView functions to handle the data as xml and use it to every action.
Also #Jelle Keizer answer should work. $this->render('/xml/createorder'); points to app/View/xml/createorder. If you need this to point to app/View/Order/xml/create just use $this->render('/Orders/xml/create');.
$this->render('/xml/createorder');
I have to display different views for mobile devices and I want to provide a simple JSON-API.
I wrote a little module for the Kohana Framework which loads different views depending on some circumstances, which should help me in this case: https://github.com/ClaudioAlbertin/Kohana-View-Factory
However, I'm not very happy with this solution because I can't set different assets for different device-types. Also, when I'd output JSON with a JSON-view, it's still wrapped in all the HTML-templates.
Now, I'm looking for a better solution. How do you handle different output formats or device-types in your MVC-applications?
I had an idea: just split the controller into two controllers: a data-controller and an output-controller.
The data-controller gets and sets data with help of the models, does
all the validating etc. It gets the data from the models and write it to a data-object
which is later passed to the view.
The output-controller loads the views and give them the data-object from the data-controller. There is an output-controller for each format or device-type: an output-controller for mobile-devices could load the mobile-views and add all the mobile-versions of stylesheets and scripts. A JSON-output-controller could load a view without all the html-template stuff and convert the data into JSON.
A little example:
<?php
class Controller_Data_User extends Controller_Data // Controller_Data defines a data-object $this->data
{
public function action_index()
{
$this->request->redirect('user/list');
}
public function action_list()
{
$this->data->users = ORM::factory('user')->find_all();
}
public function action_show($id)
{
$user = new Model_User((int) $id);
if (!$user->loaded()) {
throw new HTTP_Exception_404('User not found.');
}
$this->data->user = $user;
}
}
class Controller_Output_Desktop extends Controller_Output_HTML // Controller_Output_HTML loads a HTML-template
{
public function action_list($data)
{
$view = new View('user/list.desktop');
$view->set($data->as_array());
$this->template->body = $view;
}
public function action_show($data)
{
$view = new View('user/show.desktop');
$view->set($data->as_array());
$this->template->body = $view;
}
}
class Controller_Output_JSON extends Controller_Output // Controller_Output doesn't load a template
{
public function action_list($data)
{
$view = new View('user/list.json');
$view->users = json_encode($data->users->as_array());
$this->template = $view;
}
public function action_show($data)
{
$view = new View('user/show.json');
$view->user = json_encode($data->user);
$this->template = $view;
}
}
What do you think?
Hmm... From the 1st view it loooks strange, and somehow like fractal -- we are breaking on MVC one of our MVC -- C.
But why is this app returns so different results, based on point-of-entry (or device)?
The task of the controller is only to get the data and choose the view -- why do we need standalone logic for choosing something based on point-of-entry (device)?
I think these questions should be answered first. Somewhere could be some problem.
Also the cotroller should select only one view ideally, and dont' do "encode" or else with data, based on current output. I think all this should be in some kind of "layouts" or else. As data always the same and even different views should be the same -- only some aspects changes.