I'm trying to make a PHP template without any libraries or frameworks, in order to understand templating. The template is of a <select> that displays a list of all the countries. It was fully working before I templated it, so the issue is not with the <select> element logic.
However, the select template is not rendering to the page.
Here is my code:
country_select.php:
<?php
class CountrySelect {
static $template = 'country_select_template.php';
public static function display() {
if ( class_exists( 'View' ) ) {
// Get the full path to the template file.
$templatePath = dirname( __FILE__ ) . static::$template;
// Return the rendered HTML
return View::render( $templatePath );
}
else {
return "You are trying to render a template, but we can't find the View Class";
}
}
}
?>
country_select_template.php:
<div class="form-group col-sm-6">
<div class="select">
<span class="arr"></span>
<select data-bind="options: _countries,
optionsText: 'name',
optionsValue: 'geonameId',
value: selectedCountry,
optionsCaption: 'Country'">
</select>
</div>
</div>
view.php:
<?php
/** View.php **/
class View {
/**
* -------------------------------------
* Render a Template.
* -------------------------------------
*
* #param $filePath - include path to the template.
* #param null $viewData - any data to be used within the template.
* #return string -
*
*/
public static function render( $filePath, $viewData = null ) {
// Was any data sent through?
( $viewData ) ? extract( $viewData ) : null;
ob_start();
include ( $filePath );
$template = ob_get_contents();
ob_end_clean();
return $template;
}
}
?>
view_renderer.php:
<?php
require('view.php');
?>
Here is the relevant code from the php page where I try to render the template. Notice that the "region_select" is not yet templated, that is how my "country_select used to look.
<div class="row">
<?php
require 'scripts/back_end/views/country_select.php';
require 'scripts/back_end/views/view.php';
View::render('country_select_template.php');
?>
<div class="form-group col-sm-6">
<div class="select">
<span class="arr"></span>
<select data-bind="options: _regions,
optionsText: 'name',
optionsValue: 'name',
value: selectedCity,
optionsCaption: 'Region'">
</select>
</div>
</div>
How do I get the html in country_select_template to render to the page? The calling code that is supposed to initiate the template to render to the page is
<?php
require 'scripts/back_end/views/country_select.php';
require 'scripts/back_end/views/view.php';
View::render('country_select_template.php');
?>
I have changed the calling code to:
<?php
require 'scripts/back_end/views/country_select.php';
require 'scripts/back_end/views/view.php';
echo View::render('country_select_template.php');
?>
I see I have this error in the php error logs:
[15-Feb-2016 09:33:35 Europe/Berlin] PHP Warning:
require(scripts/back_end/views/country_select.php): failed to open
stream: No such file or directory in
/Applications/MAMP/htdocs/its_vegan/index.php on line 92 [15-Feb-2016
09:33:35 Europe/Berlin] PHP Fatal error: require(): Failed opening
required 'scripts/back_end/views/country_select.php'
(include_path='.:/Applications/MAMP/bin/php/php7.0.0/lib/php') in
/Applications/MAMP/htdocs/its_vegan/index.php on line 92
View::render returns the code. It does not print it.
So I think this should do it:
<?php
require 'scripts/back_end/views/country_select.php';
require 'scripts/back_end/views/view.php';
echo View::render('country_select_template.php');
?>
Related
I'm trying to figure out, what I am missing to make two lingual website for PHP pages.
In the root folder, I have index.php and other PHP files along with the include folder, which contains lang-switch.php, navbar.php with other PHP files, footer.php, header.php, etc. navbar.php contains:
<li class="nav-item">
<a class="nav-link" href="?lang=en">En</a>
</li>
<li class="nav-item">
<a class="nav-link" href="?lang=es">Es</a>
</li>
and lang-switch.php:
if (!isset($_SESSION['lang'])){
$_SESSION['lang'] = "en" ;
}else if (isset($_GET['lang']) && $_SESSION['lang'] != $_GET['lang'] && !empty($_GET['lang'])){
if($_GET['lang']){
$_SESSION['lang'] = "en";
} else if ($_GET['lang'] == "es") {
$_SESSION['lang'] = "es";
}
require_once "languages/" . $_SESSION['lang'] . ".php";
}
in the root folder, I also have the languages folder with two files en.php:
$lang = array(
"home" => "Home",
"aboutus" => "About Us",
"contactinfo" => "Contact Information",
"nodatafound" => "No Data Found",
);
and es.php:
$lang = array(
"home" => "Principal",
"aboutus" => "Sobre",
"contactinfo" => "Información del contacto",
"nodatafound" => "Datos no encontrados",
);
then in footer.php, which is in the same includes folder, I include:
include('lang-switch.php');
to pass in html tag:
<div><?php $lang['home'] ?> </div>
<div><?php $lang['aboutus'] ?></div>
I got:
Notice: Undefined variable: lang in
C:\xampp\htdocs\user\site\includes\footer.php on line 16
Notice: Undefined variable: lang in
C:\xampp\htdocs\user\site\includes\footer.php on line 17
with using <?php echo $lang['home'] ?>:
Warning: Illegal string offset 'aboutus' in
C:\xampp\htdocs\user\site\includes\footer.php on line 25
Warning: Illegal string offset 'home' in
C:\xampp\htdocs\user\site\includes\footer.php on line 26
Something like this would probably work for you. I tried to add comments to make it as clear as possible.
Also, this assumes you are using PHP 7.4+ and that you are running session_start() somewhere before this code starts.
I did not test this at all, but is should at least give you a start.
navbar.php
<li class="nav-item">
<a class="nav-link" href="?lang=en">En</a>
</li>
<li class="nav-item">
<a class="nav-link" href="?lang=es">Es</a>
</li>
functions.php or helpers.php or whatever you want
<?php
/**
* Get the current session language
*
* #return string
*/
function getLanguage()
{
// default to 'en' if no language is set yet
return $_SESSION['lang'] ?? 'en';
}
/**
* Set the language based on the `lang` url param
*
* #return void
*/
function setLanguage()
{
$known_languages = [
'en',
'es'
];
// make sure $_GET['lang'] is not empty
// and make sure the new language is known to us
if ( !empty($_GET['lang']) &&
$_SESSION['lang'] !== $_GET['lang'] &&
in_array( $_GET['lang'], $known_languages )
) {
$_SESSION['lang'] = $_GET['lang'];
}
}
/**
* Get the $lang variable with all the translated strings
* This should probably be a JSON file or something similar
*
* #return array
*/
function getTranslations() {
include "languages/" . getLanguage() . ".php";
/* #var array $lang*/
return $lang;
}
/**
* Get the translated string from our list of translations
* Print a translation missing message if not found
*
* #param string $string
* #return string
*/
function getTranslatedString( $string ) {
$string_values = getTranslations();
return $string_values[$string] ?? "Not Translated ( $string )";
}
/**
* Prints the translated string from our list of translations
*
* #param string $string
* #return void
*/
function printTranslatedString( $string ) {
echo getTranslatedString( $string );
}
The translation files stay as you have them, even though you may probably make use of JSON or something similar.
Then, you should run the setLanguage() function somewhere before the output of your HTML starts otherwise you will end up with warnings like:
Warning: Cannot modify header information - headers already sent by
(output started at ...
And then, in your HTML you should be able to use it like:
<div>
<a href="index.php" class="text-dark">
<?php printTranslatedString('home') ?>
</a>
</div>
<div>
<a href="about_us.php" class="text-dark">
<?php printTranslatedString('aboutus') ?>
</a>
</div>
I'm loading the main view within the controller home as following:
class Home
{
public static function login()
{
Views::load('frontend/index', ['view' => 'account/login', 'name' => 'Stackoverflow']);
}
}
Now as you can see the view being loaded is frontend/index but I also say what is the view that must be loaded afterwards account/login.
So in the frontend/index.php file I have the following:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<?php Views::load($view); ?>
<?php echo $name; // prints 'stackoverflow' ?>
</body>
</html>
What happens is that I'm able to access the variable $name in the frontend/index file but not in the account/login file.
The account/login.php file contains:
<h2>Hello: <?php echo $name; // shows error saying 'Undefined variable: name' ?></h2>
The error just tells me that the variable is not being cached/stored.
Finally my view class has the following structure:
<?php
class Views
{
public static function load($file, $data = null)
{
if(file_exists(VIEWS_PATH . $file . '.php') == FALSE)
throw new Exception('View not found');
if($data != null)
extract($data, EXTR_SKIP);
ob_start();
require_once(VIEWS_PATH . $file . '.php');
$content = ob_get_contents();
ob_end_clean();
// prints the view
echo $content;
}
}
What can I do in order to allow the variables sent by controller to be cached/stored and called in multiple views?
In my inc/contact-form-processor.php files I set
$form_complete = FALSE;
in my template-contact.php file I have
<?php
/*
Template Name: Contact Page
*/
?>
<?php require_once 'inc/contact-form-processor.php'; ?>
<?php get_header();
$SidebarPosition = sidebar_position()[0];
$IndivSidebarPosition = sidebar_position()[1];
$DefaultSidebarPosition = sidebar_position()[2];
?>
<div class="container">
<div class="row">
<?php if ( $SidebarPosition == 'left' ) {
get_template_part( 'layouts/contact/left', 'sidebar' );
}
if ( $SidebarPosition == 'right' ) {
get_template_part( 'layouts/contact/right', 'sidebar' );
}
if ( ( $IndivSidebarPosition == 'none' ) || ( $IndivSidebarPosition == 'default' and $DefaultSidebarPosition == 'none' ) ) {
get_template_part( 'layouts/contact/no', 'sidebar' );
}
?>
<?php echo $IndivSidebarPosition = sidebar_position()[1]; ?>
</div>
</div>
<?php get_footer(); ?>
I thought that by using require once and then referencing the contact-form-processor file $form_complete would be available in this file and the subsequent default
get_template_part( 'layouts/contact/right', 'sidebar' );
which displays a contact form based on the condition
<div id="contact_form">
<?php if($form_complete === FALSE) { ?>
<form>
.. Form ...
</form>
<?php } ?>
</div>
However, the form is not displayed and when I check for the $form_complete variable it is empty. How can I pass the variable through to both the files, I have read that I can use
You can use the WordPress locate_template function within PHP’s
include(). It’s done like this:
include(locate_template('your-template-name.php'));
All of the variables available in your current script will be available in that
template file now too.
But I am not sure which file that code goes in and which file it is suppose to reference.
your issues relate to scope. There is no magic trick to the snippet
include(locate_template('your-template-name.php'));
Locate template just returns the filename (it looks in the theme to find the appropriate file, generally used for allowing overwriting by child themes). What is relevant to you is include will load the file into the same scope as the function/ line that called it.
So lets look at this:
$a= 'im outside scope';
$b = 'im outside scope but get passed into the function so i can be used';
function sample($var){
$c= 'in scope';
$d= $var;
include 'template.php';
}
sample($b);
Template.php
<?php
echo $a; // ''
echo $b; // ''
echo $c; // 'in scope'
echo $d; // 'im outside scope but get passed into the function so i can be used'
So if you use get_template_part(), this is a function, only variables you have passed into the function (either in the arguments, by calling globals, class props) will be available in the template, get template part does not accept arguments you can use for this.
So the solution is to replace your get_template_part() calls with include calls. This way you have variables in the same scope.
I can't passing the variables in view page
this is my controller code
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Management extends CI_Controller {
public function __construct()
{
parent::__construct();
if($this->session->userdata('level') != 1){
redirect('');
}
}
public function hotels()
{
$this->load->model('model_hotels');
$ss['most_view_hotels'] = '23';
$this->load->view("management/header_mng_view");
$this->load->view('management/hotels_mng_view' , $ss , true);
$this->load->view("management/footer_mng_view");
}
}
this is the error
A PHP Error was encountered
Severity: Notice
Message: Undefined variable: most_view_hotels
Filename: management/hotels_mng_view.php
Line Number: 22
hotels_mng_view.php
<?php
echo $most_view_hotels;
foreach($most_view_hotels as $value):
?>
<div class="row">
<div class="cell en">adsf</div>
<div class="cell en">1212</div>
<div class="cell en">12</div>
<div class="cell en">32</div>
</div>
<?php endforeach;?>
In your controller:
$ss['most_view_hotels'] = '23';
$this->load->view("management/header_mng_view");
$this->load->view('management/hotels_mng_view',$ss);
$this->load->view("management/footer_mng_view");
In your view:
<?php echo $most_view_hotels?>
In you're template you would refer to the item as...
$most_view_hotels
So we need to verify this is what you are doing in the view management/hotels_mng_view.php
As said on Codeigniter manual:
There is a third optional parameter lets you change the behavior of the function
so that it returns data as a string rather than sending it to your browser.
This can be useful if you want to process the data in some way. If you set
the parameter to true (boolean) it will return data.
The default behavior is false, which sends it to your browser.
What this means is that your line
$this->load->view('management/hotels_mng_view' , $ss , true);
actually returns result of that view as string to php instead of sending it to browser.
The answer to your actual question ( undefined variable ), relies in in management/hotels_mng_view.php, possibly a typo.
I think the problem is with third parameter, ie true.
As codeigniter documentation says:
There is a third optional parameter lets .....
Remember to assign it to a variable if you want the data returned
And in your code you are not assigning, returned data to any code.
I suggest you to try something like this:
public function hotels()
{
$this->load->model('model_hotels');
$data = array();
$ss['most_view_hotels'] = '23';
$data['header'] = $this->load->view("management/header_mng_view",,true);
$data['body'] = $this->load->view('management/hotels_mng_view' , $ss , true);
$data['footer'] = $this->load->view("management/footer_mng_view");
$this->load->view("template",$data);
}
/*template view file*/
<?php
echo $header;
echo $body;
echo $footer;
?>
Hope this helps.
I create a dynamic website using php/mysql. my template for print data using pure php code + html . now , better, optimized, faster way for Structure of mix php + html ? ( without template engine )
for each page i have : ex.1 ( php code before wrapper )
<?PHP include ( DEFINE_SITE . '/templates/header.php' );
// isset : post : get : SELECT : INSERT ... Or ANY CODE OF PHP
?>
<div id="wrapper">
<div id="leftsidebar"><?PHP // Left DATA ?></div>
<div id="centercontent">
<?PHP // while {} Print result of php ANY LOOPS ..... ?>
</div>
<div id="rightsidebar"><?PHP // Right DATA ?></div>
</div>
<?PHP include ( DEFINE_SITE . '/templates/footer.php' ); ?>
for each page i have : ex.2 ( after side bar and before center content )
<?PHP include ( DEFINE_SITE . '/templates/header.php' );
<div id="wrapper">
<div id="leftsidebar"><?PHP // Left DATA ?></div>
<?PHP // isset : post : get : SELECT : INSERT ... Or ANY CODE OF PHP ?>
<div id="centercontent">
<?PHP // while {} Print result of php ANY LOOPS ..... ?>
</div>
<div id="rightsidebar"><?PHP // Right DATA ?></div>
</div>
<?PHP include ( DEFINE_SITE . '/templates/footer.php' );?>
Which is better? e.x 1 or e.x 2 ? your opinion ?
why the aversion to a template engine? In reality, PHP is a template engine, but perhaps if you checked a few out (like Twig) it might help you in designing something more flexible, fast and responsive without the template engine.... or you might start to love it, like me ;-)
I suggest that you keep your HTML and PHP separated. If you don't want to use a template system why don't you do it like this?
Make a HTML template:
<div id="wrapper">
<div id="leftsidebar">{LEFT}</div>
<div id="centercontent">{CONTENT}</div>
<div id="rightsidebar">{RIGHT}</div>
</div>
In a different php file:
$content = file_get_contents('template.html');
$templateVars = array();
startVar('CONTENT');
echo '<p>This is the content.</p>'; // New template vars are also allowed
endVar('CONTENT');
echo replaceTemplateVars($content);
/**
* Replace template variables recursively with the key-value pairs that are in the $templateVars queue
* #param string $content (default NULL) is code to convert.
* #return the string that is replaced
*/
function replaceTemplateVars($content = NULL)
{
global $templateVars;
$matches = array();
preg_match_all("/{[A-Z0-9_:]*}/", $content, $matches); // $matches = All vars found in your template
foreach($matches[0] as $key)
{
if(isset($templateVars[substr($key, 1, -1)]))
{
$content = str_replace($key, $templateVars[substr($key, 1, -1)], $content); // Substitute template var with contents
}
else
{
$content = str_replace($key, "", $content); // Remove empty template var from template
}
}
// Check if the replacement has entered any new template variables, if so go recursive and replace these too
if(preg_match_all("/{[A-Z0-9_:]*}/", $content, $matches))
{
$content = replaceTemplateVars($content);
}
return $content;
}
/**
* Start output buffer for a template key
* #param string $key is not used. Only for overview purposes
*/
function startVar($key)
{
ob_start();
}
/**
* End output buffer for a template key and store the contents of the output buffer in the template key
* #param string $key
*/
function endVar($key)
{
setVar($key, ob_get_contents());
ob_end_clean();
}
/**
* #param string $key to store value in
* #param mixed $value to be stored
*/
function setVar($key, $value)
{
global $templateVars;
$templateVars[$key] = $value;
}
And this is what you get on your screen:
<div id="wrapper">
<div id="leftsidebar"></div>
<div id="centercontent"><p>This is the content.</p></div>
<div id="rightsidebar"></div>
</div>
So preprocessing is done first and at the end all html code is outputted.
Of course you could add functions like startPrependVar() and startAppendVar() and wrap everything into a Template class to get rid of the global scope.