I am using the following template library. What I am trying to do is to load one or more upper views inside a template,as an array so that I can easily load them in the template with a for-each loop.
This is a simple example on how it can be used on a controller:
function index() {
$data['title'] = 'My page title';
$partials = array('content'=>'c_location'); //Load view about inside the template.
$this->template->load('main', $partials, $data);
}
On the View you have an html like:
<html>
....
<?=$content?>
...
</html?>
This is what I am trying to use:
Controller:
$partials = array('content'=>'c_location',
array(
'first_upper_content'=>'1_u_location','second_upper_content'=>'2_u_location'
)
);
So for example I could pass for upper_content, a top header as "first_upper_content" and a slide for "second_upper_content" and then the remainder of the content for "content".
Html:
...
<?=$upper_content?>
<--if upper_content is a array,
I could display each content with a for loop-->
<?=$content?>
When I try everything I get:
Message: pathinfo() expects parameter 1 to be string, array given
Filename: core/Loader.php
Line Number: 759
How can I implement this? I am thinking of modifying the
// Load views into var array
Inside the Template.php & adding a foreach loop to the html;
This is what you want:
// Load views into var array
foreach(array_flat($view) as $key => $file)
Include the "array_flat" function call in Template.php, as shown above.
You will need to define this function. You can do this in any auto-loaded helper, or even include it the own Template class (in this case, you should call it as $this->array_flat in the code above). Here is it:
function array_flat($array)
{
$result = array();
foreach ($array as $key => $value)
{
if (is_array($value))
{
$result = array_merge($result, array_flat($value));
}
else
{
$result[$key] = $value;
}
}
return $result;
}
What you're trying to do is not possible with that library.
The error you received indicates the CI Loader class is being passed invalid data. So taking a look at the library's load function, your code will fail right here:
// Load views into var array
foreach($view as $key => $file)
{
$tpl[$key] = $this->CI->load->view($file, $vars, TRUE);
}
The library passes your nested array in $partials directly to the CI Loader. With your data, this line works out to:
$tpl[0] = $this->CI->load->view(array(
'first_upper_content'=>'1_u_location','second_upper_content'=>'2_u_location'
), vars, TRUE);
You can see in the CI user guide that isn't valid. It seems to me that your options are to either overhaul the library or change your approach.
Just make your main template view, and have something like this in it:
$this->load->view('header');
if (is_array($page) {
foreach($page as $key=>$val){
$this->load->view($key, $val); // $val being optional parameters
}
} else {
$this->load->view($page);
}
$this->load->view('footer');
In the controller, $data['page'] would contain view info, either its name (string) or an array of names to call in order. Of course, you'll have to have premade views with those names.
This was from the top of my head, but it should do what you wanted. This is done without template libraries, just plain CI.
To load partials, all you need to do is use:
$this->load->view('partial_location',$data);
inside your main view. So if you have a main view called home_page.php, and you want to load partials for headers and footers, you can just use:
$this->load->view('header');
// Main page layout
$this->load->view('footer');
If you want to use custom or different data inside your partials, just define them in your controller:
$data = array(
'content' => 'about',
'header_content' => 'Welcome to the site!',
'footer_content' => 'Made by me!'
);
And in your main view file:
$this->load->view('header',$header_content);
// would echo 'Welcome to the site!'
echo $content;
// would echo 'about'
$this->load->view('footer',$footer_content);
// would echo 'Made by me!'
Related
I know that you can use extract() to achieve this however my circumstances are as followed:
I am building a very small basic MVC framework for personal projects and I have this in my controller:
public function index(){
$data = [
'title' => 'Welcome'
];
$this->view('pages/index', $data);
}
As you can see this passes the data array into the view and you can echo it like:
echo $data['title'];
But I want to echo it like echo $title; I know that extract() can do this but that means I have to manually put extract($data); at the top of every page which isnt the end of the world but I was just curious if there was a way it could be done automatically? I have tried to use extract by putting it inside the view function but that did not work. I have also tried to use extract by putting it in the header file that is required_once in index.php (thus making the header file a static header thats always required) but neither has worked so any advice would be great.
Here is the code for the view function as requested:
public function view($view, $data = []){
if(file_exists('../../views/'.$view.'.php')){
require_once '../../views/'.$view.'.php';
} else {
die('View does not exist');
}
}
Simple that is it ,use compact and extract function
index method
public function index(){
$title='Welcome';
$this->view('pages/index', compact('title'));
}
Wiew method
public function view($view, $data = []){
extract($data);
if(file_exists('../../views/'.$view.'.php')){
require_once '../../views/'.$view.'.php';
} else {
die('View does not exist');
}
}
In html
<h1> hello <?php echo $title; ?></h1>
Here is where I went wrong, I put extract in the view method before and it didn't work.
However the advice here was to put it in the view function and I now understand that I put the extract function after require_once '../../views/'.$view.'.php'; I just put extract before that line of code and it is now working!
Here is my controller:
class CommonController extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->model('common_model'); //load your model my model is "common model"
}
public function add_work(){
$names = $_POST['name'];
$works = $_POST['work'];
$allValues = array(); // array to contains inserted rows
foreach($names as $key => $name){
$name= "your specified name";
$insertdata = array();
$insertdata['work'] = $works[$key];
$insertdata['name'] = $name;
$this->common_model->insert($insertdata);
array_push($allValues,$insertdata);
//$insert = mysql_query("INSERT INTO work(name,work) values ( '$name','$work')");
}
foreach($allValues as $insertRow){
echo $insertRow['work'];
echo $insertRow['name'];//this shows data well. but how to pass data in view.php
}
//view code will add here to show data in browser
}
Basically I want to pass all data to view.php for printing or exporting purpose. How can I do so.
To load a view you should do like this.
$this->load->view("filename");
If you want to pass data to view, you should do like this.
$this->load->view("filename",$data);
$data should have all parameters which you want to print in view.
The syntax goes like this.
$this->load->view("filename","data to view","Returning views as data(true / false");
If third parameter is true, view will come as data. It will not go to browser as output.
Edit:
Change
$this->load->view('print_view',$insertdata);
to
$data['insertdata'] = $insertdata;
$this->load->view('print_view',$data);
For more info, check this link
How CI Classes Pass Information and Control to Each Other
Calling Views
We will see.how the controller calls a view and passes data to it:
First it creates an array of data ($data) to pass to the view; then it loads and calls the view in the same expression:
$this->load->view('testview', $data);
You can call libraries, models, plug-ins, or helpers from within any controller, and models and libraries can also call each other as well as plug-ins and helpers.
However, you can't call one controller from another, or call a controller from a
model or library. There are only two ways that a model or a library can refer back to a controller:
Firstly, it can return data. If the controller assigns a value like this:
$foo = $this->mymodel->myfunction();
and the function is set to return a value, then that value will be passed to the variable $foo inside the controller.
//sample
public function display()
{
$data['text_to_display'] = $this->text_to_display;
$data['text_color'] = $this->text_color;
$this->load->view('display_view',$data);
}
Adding Dynamic Data to the View
Data is passed from the controller to the view by way of an array or an object in the second parameter of the view
loading method. Here is an example using an array:
$data = array(
’title’ => ’some’,
’heading’ => ’another some’,
’message’ => ’and another some’
);
$this->load->view(’view’, $data);
And here’s an example using an object:
$data = new Someclass();
$this->load->view(’view’, $data);
Sending Multiple Dimensional array
if we pull data from your database it will typically be
in the form of a multi-dimensional array.
<?php
class foo extends CI_Controller {
public function index()
{
$data[’Books’] = array(’POEAA’, ’TDD’, ’Clean C’);
$data[’title’] = "Title";
$data[’heading’] = "Heading";
$this->load->view(’view’, $data);
}
}
in view
<html>
<head>
<title><?php echo $title;?></title>
</head>
<body>
<h1><?php echo $heading;?></h1>
<h3>My Books List</h3>
<ul>
<?php foreach ($Books as $item):?>
<li><?php echo $item;?></li>
<?php endforeach;?>
</ul>
</body>
</html>
More Learning
NOTE:
There is a third optional parameter lets you change the behavior of the method so that it returns data as a string rather
than sending it to your browser.The default behavior is false, which sends it to your browser. Remember to
assign it to a variable if you want the data returned:
$string = $this->load->view(’view’, ’’, TRUE);
Above will not solve your problem directly but definetly help in understanding concepts.
Can someone help me out with this piece of code.
I have a template set up like this:
system/core/Loader.php :
public function template($view, $vars = array(), $return = FALSE)
{
$template = $this->view('includes/header', array(), $return);
$template = $this->view('includes/navigation', array(), $return);
$template .= $this->view($view, $vars, $return);
$template .= $this->view('includes/footer', array(), $return);
if ($return)
{
return $template;
}
}
It works perfect but when I try this to display a dynamic title, it doesn't want to display the title. I guess because the data is passed to content_home rather then to includes/header:
my controller:
public function home() {
$this->load->model("model_get");
$data["page_title"] = "Home Page";
$data["results"] = ($this->model_get->getData("home"));
$this->load->template("content_home",$data);
}
includes/header.php
<title><?php echo (isset($page_title)) ? $page_title : 'Default title text'; ?> </title>
Any ideas how to work it out ??
Thanks : )
I've implemented dynamic titles in an even simpler way:
Controller:
$data['header_data'] = array('title'=>'Foo bar');
$data['page'] = "login";
$this->load->view('template', $data);
View - template.php
$this->load->view('includes/header', $header_data);
$this->load->view('pages/'.$page);
$this->load->view('footer');
We have two folders in views - includes and pages.
includes folder: header.php, footer.php (etc..)
pages folder: login.php, profile.php (etc..)
views/includes/header.php
<!DOCTYPE html>
<html>
<head>
<title><?=$title?></title>
</head>
...
This way, you can use the same template to load all your pages. You may send data other than the title too!
In your views folder make a template file like this,
we'll call it template_web.php
<?php
// the preset view files for your template
$this->load->view('includes/header');
$this->load->view('includes/navigation');
// in this example we will have our content view files in a folder called 'web'
$templatefolder = 'web/';
// this is where you can pass in 'content' view files that are unique for the page
// this example has 3 placeholders you can put as many as you need
if(isset($content01))
$this->load->view($templatefolder.$content01);
if(isset($content02))
$this->load->view($templatefolder.$content02);
if(isset($content03))
$this->load->view($templatefolder.$content03);
// preset template footer file
$this->load->view('includes/footer');
ok now in your controller methods
// data you want to pass to your header
$data['page_title'] = 'Awesome Home Page';
// other data
$data['bigbagofdata'] = $this->bigBagOf->awesomeData ;
// the name of the view files
$data['content01'] = 'homepage';
$data['content02'] = 'homepage_banner';
// your template
$this->load->view( 'template_web', $data );
data can be passed to any of the view files. and its very easy to set up different templates this way.
Try
$template = $this->view('includes/header', $view, $return);
instead of
$template = $this->view('includes/header', array(), $return);
If you want to access your $data["page_title"] variable in your header view, then you should pass it into your header view:
change this line:
$template = $this->view('includes/header', array(), $return);
to
$template = $this->view('includes/header', $vars, $return);
I think MrMarchello finds oout the real issue. the issue is not passing the $data variable to header.
$data['title'] = "Edit User";
$this->load->view('template/header', $data);
$this->load->view('user/edit_member', $data);
$this->load->view('template/footer');
Hope this help you a little bit.
In your config file add this parameter:
$config['pageTitle'] = 'Book Store';
In template page :
<title><?=$this->config->config["pageTitle"]?></title>
In any page you like change this config in action:
$this->config->config["pageTitle"] = $detail[0]['Title'];
Every time you can change the title easily.
So I'm setting up a system that has a lot of emails, and variable replacement within it, so I'm writing a class to manage some variable replacement for templates stored in the database.
Here's a brief example:
// template is stored in db, so that's how this would get loaded in
$template = "Hello, %customer_name%, thank you for contacting %website_name%";
// The array of replacements is built manually and passed to the class
// with actual values being called from db
$replacements = array('%customer_name%'=>'Bob', '%website_name%'=>'Acme');
$rendered = str_replace(array_keys($replacements), $replacements, $template);
Now, that works well and good for single var replacements, basic stuff. However, there are some places where there should be a for loop, and I'm lost how to implement it.
The idea is there'd be a template like this:
"hello, %customer_name%, thank you for
requesting information on {products}"
Where, {products} would be an array passed to the template, which the is looped over for products requested, with a format like:
Our product %product_name% has a cost
of %product_price%. Learn more at
%product_url%.
So an example rendered version of this would be:
"hello, bob, thank you for requesting
information on:
Our product WidgetA has a cost of $1.
Learn more at example/A
Our product WidgetB has a cost of $2.
Learn more at example/B
Our product WidgetC has a cost of $3.
Learn more at example/C.
What's the best way to accomplish this?
Well, I really dont see the point in a template engine that uses repalcements/regex
PHP Is already a template engine, when you write <?php echo $var?> its just like doing <{$var}> or {$var}
Think of it this way, PHP Already translates <?php echo '<b>hello</b>'?> into <b>hello</b> by its engine, so why make it do everything 2 times over.
The way i would implement a template engine is like so
Firstly create a template class
class Template
{
var $vars = array();
function __set($key,$val)
{
$this->vars[$key] = $val;
}
function __get($key)
{
return isset($this->vars[$key]) ? $this->vars[$key] : false;
}
function output($tpl = false)
{
if($tpl === false)
{
die('No template file selected in Template::output(...)');
}
if(!file_exists(($dir = 'templates/' . $tpl . '.php')))
{
die(sprintf('Tpl file does not exists (%s)',$dir));
}
new TemplateLoader($dir,$this->vars);
return true;
}
}
This is what you use in your login such as index.php, you will set data just like an stdClass just google it if your unsure. and when you run the output command it sends the data and tpl to the next class below.
And then create a standalone class to compile the tpl file within.
class TemplateLoader
{
private $vars = array();
private $_vars = array(); //hold vars set within the tpl file
function __construct($file,$variables)
{
$this->vars = $variables;
//Start the capture;
ob_start();
include $file;
$contents = ob_get_contents();
ob_end_clean(); //Clean it
//Return here if you wish
echo $contents;
}
function __get($key)
{
return isset($this->vars[$key]) ? $this->vars[$key] : (isset($this->_vars[$key]) ? $this->_vars[$key] : false) : false;
}
function __set($key,$val)
{
$this->_vars[$key] = $val;
return true;
}
function bold($key)
{
return '<strong>' . $this->$key . '</string>';
}
}
The reason we keep this seperate is so it has its own space to run in, you just load your tpl file as an include in your constructor so it only can be loaded once, then when the file is included it has access to all the data and methods within TemplateLoader.
Index.php
<?php
require_once 'includes/Template.php';
require_once 'includes/TemplateLoader.php';
$Template = new Template();
$Template->foo = 'somestring';
$Template->bar = array('some' => 'array');
$Template->zed = new stdClass(); // Showing Objects
$Template->output('index'); // loads templates/index.php
?>
Now here we dont really want to mix html with this page because by seperating the php and the view / templates you making sure all your php has completed because when you send html or use html it stops certain aspects of your script from running.
templates/index.php
header
<h1><?php $this->foo;?></h1>
<ul>
<?php foreach($this->bar as $this->_foo):?>
<li><?php echo $this->_foo; ?></li>
<?php endforeach; ?>
</ul>
<p>Testing Objects</p>
<?php $this->sidebar = $this->foo->show_sidebar ? $this->foo->show_sidebar : false;?>
<?php if($this->sidebar):?>
Showing my sidebar.
<?php endif;?>
footer
Now here we can see that were mixing html with php but this is ok because in ehre you should only use basic stuff such as Foreach,For etc. and Variables.
NOTE: IN the TemplateLoader Class you can add a function like..
function bold($key)
{
return '<strong>' . $this->$key . '</string>';
}
This will allow you to increase your actions in your templates so bold,italic,atuoloop,css_secure,stripslashs..
You still have all the normal tools such as stripslashes/htmlentites etc.
Heres a small example of the bold.
$this->bold('foo'); //Returns <strong>somestring</string>
You can add lots of tools into the TempalteLoader class such as inc() to load other tpl files, you can develop a helper system so you can go $this->helpers->jquery->googleSource
If you have any more questions feel free to ask me.
----------
An example of storing in your database.
<?php
if(false != ($data = mysql_query('SELECT * FROM tpl_catch where item_name = \'index\' AND item_save_time > '.time() - 3600 .' LIMIT 1 ORDER BY item_save_time DESC')))
{
if(myslq_num_rows($data) > 0)
{
$row = mysql_fetch_assc($data);
die($row[0]['item_content']);
}else
{
//Compile it with the sample code in first section (index.php)
//Followed by inserting it into the database
then print out the content.
}
}
?>
If you wish to store your tpl files including PHP then that's not a problem, within Template where you passing in the tpl file name just search db instead of the filesystem
$products = array('...');
function parse_products($matches)
{
global $products;
$str = '';
foreach($products as $product) {
$str .= str_replace('%product_name%', $product, $matches[1]); // $matches[1] is whatever is between {products} and {/products}
}
return $str;
}
$str = preg_replace_callback('#\{products}(.*)\{/products}#s', 'parse_products', $str);
The idea is to find string between {products} and {products}, pass it to some function, do whatever you need to do with it, iterating over $products array.
Whatever the function returns replaces whole "{products}[anything here]{/products}".
The input string would look like that:
Requested products: {products}%product_name%{/products}
I have simple template that's html mostly and then pulls some stuff out of SQL via PHP and I want to include this template in three different spots of another php file. What is the best way to do this? Can I include it and then print the contents?
Example of template:
Price: <?php echo $price ?>
and, for example, I have another php file that will show the template file only if the date is more than two days after a date in SQL.
The best way is to pass everything in an associative array.
class Template {
public function render($_page, $_data) {
extract($_data);
include($_page);
}
}
To build the template:
$data = array('title' => 'My Page', 'text' => 'My Paragraph');
$Template = new Template();
$Template->render('/path/to/file.php', $data);
Your template page could be something like this:
<h1><?php echo $title; ?></h1>
<p><?php echo $text; ?></p>
Extract is a really nifty function that can unpack an associative array into the local namespace so that you can just do stuff like echo $title;.
Edit: Added underscores to prevent name conflicts just in case you extract something containing a variable '$page' or '$data'.
Put your data in an array/object and pass it to the following function as a second argument:
function template_contents($file, $model) {
if (!is_file($file)) {
throw new Exception("Template not found");
}
ob_start();
include $file;
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
Then:
Price: <?php echo template_contents("/path/to/file.php", $model); ?>