So, I've created a webshop system. It all worked perfectly, except for that projects can always be improved. So someone told me that 'Templating' and 'Routing' would be nice improvements. I've written classes for both of them, and they work fine! Now, I do wish to know how to combine them? How can I combine these classes so that data from the routing (to determine the content) needs to be placed inside the template. How would I do this?
Templating class:
class Template
{
private $assignedValues = array();
private $tpl;
/*
** #description Creates one single instance of itself and checks whether the template file exists.
** #param $path [string] This is the path to the template
*/
public function __construct($_path = '')
{
if(!empty($_path)){
if(file_exists($_path)){
$this->tpl = file_get_contents($_path);
}
else{
echo '<b>Template Error:</b> File Inclusion Error.';
}
}
}
/*
** #description Assign a value to a part in the template.
** #param $_searchString [string] This is the part in the template that needs to be replaced
** #param $_replaceString [string] This is the code/text that will replace the part in the template
*/
public function assign($_searchString, $_replaceString)
{
if(!empty($_searchString)){
$this->assignedValues[strtoupper($_searchString)] = $_replaceString;
}
}
/*
** #description Shows the final result of the page.
*/
public function show()
{
if(count($this->assignedValues > 0)){
foreach ($this->assignedValues as $key => $value) {
$this->tpl = str_replace('{'.$key.'}', $value, $this->tpl);
}
}
echo $this->tpl;
}
/*
** #description Quickly load a part of the page
** #param $quickLoad [string] This is the name of the file that will be loaded and assigned
** #param $_searchString [string] This is the part in the template that needs to be replaced
*/
public function quickLoad($_searchString, $part)
{
if(file_exists(INCLUDES.DS.$part.'.php')){
$this->assign($_searchString,include(INCLUDES.DS.$part.'.php'));
}
else{
return "That file does not exist!";
}
}
}
And the routing class:
class Route
{
protected $controller = 'App';
protected $method = 'Call';
protected $params = [];
/*
** #description Loads the classes and methods which are referred to.
*/
public function __construct()
{
$url = $this->parseUrl();
if($this->checkUrl())
{
unset($url[0]);
if(isset($url[1]))
{
if (file_exists('core/classes/' . $url[1] . '.class.php'))
{
$this->controller = $url[1];
unset($url[1]);
}
}
require_once('core/classes/' . $this->controller . '.class.php');
$this->controller = new $this->controller;
if (isset($url[2]))
{
if (method_exists($this->controller, $url[2]))
{
$this->method = $url[2];
unset($url[2]);
}
}
$this->params = $url ? array_values($url) : [];
$this->arrayUrl($this->params);
call_user_func_array([$this->controller, $this->method], $this->params);
}
}
/*
** #description Check whether the URL part contains a string
*/
public function checkUrl($index = '0',$value = 'Route'){
$url = $this->parseUrl();
if($url[$index] == $value){
return true;
}
return false;
}
/*
** #description Splits the url into pieces.
*/
protected function parseUrl()
{
if(isset($_GET['url']))
{
return $url = explode('/', filter_var(rtrim(urldecode($_GET['url']), '/'), FILTER_SANITIZE_URL));
}
}
/*
** #description Sets arrays in routes.
*/
protected function arrayUrl($params = array())
{
foreach($params as $index => $param)
{
if (preg_match('/>/',$param))
{
$newParam = explode('>', $param);
unset($this->params[$index]);
$this->params['fields'][$newParam[0]] = $newParam[1];
}
else{
unset($this->params[$index]);
$this->params[] = $param;
}
}
print_r($this->params);
}
}
I can access my routes by URL's like this:
http://localhost:8080/Webshop/Route/User/logout
With: Class & Method.
This is a simple example that I already use, because no data needs to be showed using this method. It only logs out the user that is logged in. After that, you get redirected to the home page. But how could I use routing for other pages? For example, update some user data without having to create an update file?
EDIT:
This is an example of a page I use now (index.php):
<?php
/*
** #description Includes config.php once so that we can use classes, defines etcetera.
*/
require_once('core/preferences/config.php');
/*
** #description Instanciate new route object.
*/
$route = new Route();
/*
** #description Check if a route isset. When not, continue, else: run route
*/
if(!$route->checkUrl())
{
/*
** #description Instanciate new template object.
*/
$template = new Template(TEMPLATES_PATH .'/camerashop24.tpl.html');
/*
** #description Assign values.
*/
$template->assign('title', 'Home');
$template->assign('root', '');
$template->quickLoad('loginout', 'loginout');
$template->quickLoad('title_block', 'title_block');
$template->quickLoad('cart','cart');
$template->quickLoad('menu', 'menu');
$template->assign('page', 'Home');
$breadcrumbs = new Breadcrumbs($_SERVER["REQUEST_URI"],'');
$template->assign('breadcrumbs', $breadcrumbs->data());
$content = "";
foreach(explode(",",Config::get('settings/front_page_cat')) as $item) {
$content .= "<div id='blue-box' class='blue-box'><h2 style='color: white;'>" . strtoupper($item) . "</h2></div>";
$category = new Category();
$category->find($item);
if($category->exists($item)){
foreach (explode(",",$category->data()->products) as $item) {
$product = new Product($item);
$product->find($item);
$content .= '' . $product->showProduct($product->data()->type,$product->data()->name,$product->data()->price) . '';
}
}
}
$template->assign('text', $content);
$template->quickLoad('footer','footer');
/*
** #description Showing content.
*/
$template->show();
}
But, what I want as an answer, how can I show the data from the routing (select users profile for example) in this template without having to create a page for it like this one.
I think you're gonna have to modify your Routing class. Ideally you want the routing class to give YOU the ability to tell it what controller the route should use. Th controller you select would act as the middle man for processing. Each controller's method would represent a route, something like this:
/**
* URL = www.test.com/index.php/home
* URL = www.test.com/index.php/about
*
* ROUTE Class:
* - should define routes. Example:
*
* $route["/home"] = MainController/home
* $route["/about"] = MainController/about
* $route["/*"] = MainController/*
*
*/
class MainController
{
public function home()
{
$template = new Template(TEMPLATES_PATH . 'home.tpl.html');
$template->show();
}
public function about()
{
$template = new Template(TEMPLATES_PATH . 'about.tpl.html');
$template->show();
}
}
Related
Here am getting an error like Fatal error: Cannot declare class ABC, because the name is already in use in while listing all controller names and functions in project.In order to get i got an library from surroundings and placed it.library looks like this
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
/***
* File: (Codeigniterapp)/libraries/Controllerlist.php
*
* A simple library to list al your controllers with their methods.
* This library will return an array with controllers and methods
*
* The library will scan the "controller" directory and (in case of) one (1) subdirectory level deep
* for controllers
*
* Usage in one of your controllers:
*
* $this->load->library('controllerlist');
* print_r($this->controllerlist->getControllers());
*
* #author Peter Prins
*/
class ControllerList {
/**
* Codeigniter reference
*/
private $CI;
/**
* Array that will hold the controller names and methods
*/
private $aControllers;
// Construct
function __construct() {
// Get Codeigniter instance
$this->CI = get_instance();
// Get all controllers
$this->setControllers();
}
/**
* Return all controllers and their methods
* #return array
*/
public function getControllers() {
return $this->aControllers;
}
/**
* Set the array holding the controller name and methods
*/
public function setControllerMethods($p_sControllerName, $p_aControllerMethods) {
$this->aControllers[$p_sControllerName] = $p_aControllerMethods;
}
/**
* Search and set controller and methods.
*/
private function setControllers() {
// Loop through the controller directory
foreach(glob(APPPATH . 'controllers/*') as $controller) {
// if the value in the loop is a directory loop through that directory
if(is_dir($controller)) {
// Get name of directory
$dirname = basename($controller, 'EXT');
// Loop through the subdirectory
foreach(glob(APPPATH . 'controllers/'.$dirname.'/*') as $subdircontroller) {
// Get the name of the subdir
$subdircontrollername = basename($subdircontroller, EXT);
// Load the controller file in memory if it's not load already
if(!class_exists($subdircontrollername)) {
$this->CI->load->file($subdircontroller);
}
// Add the controllername to the array with its methods
$aMethods = get_class_methods($subdircontrollername);
$aUserMethods = array();
foreach($aMethods as $method) {
if($method != '__construct' && $method != 'get_instance' && $method != $subdircontrollername) {
$aUserMethods[] = $method;
}
}
$this->setControllerMethods($subdircontrollername, $aUserMethods);
}
}
else if(pathinfo($controller, PATHINFO_EXTENSION) == "php"){
// value is no directory get controller name
$controllername = basename($controller, 'EXT');
// Load the class in memory (if it's not loaded already)
if(!class_exists($controllername)) {
var_dump($controller);
$this->CI->load->file($controller);
}
// Add controller and methods to the array
$aMethods = get_class_methods($controllername);
//var_dump($aMethods);
$aUserMethods = array();
if(is_array($aMethods)){
foreach($aMethods as $method) {
if($method != '__construct' && $method != 'get_instance' && $method != $controllername) {
$aUserMethods[] = $method;
}
}
}
$this->setControllerMethods($controllername, $aUserMethods);
}
}
}
}
// EOF
Here am getting error in the line $this->CI->load->file($controller);.when i call this library in my controller am getting error what i mentioned in title.Is it a problem of php version,Here it is telling that controller class name is already called so it cannot be call again.
I am trying to create a custom router.
This is what I have so far:
<?php
/**
* Created by PhpStorm.
* User: antony
* Date: 5/30/16
* Time: 3:31 PM
*/
namespace Fab\Router;
class Router
{
private $_getUri = array();
private $_getController = array();
private $_getMethod = array();
private $_postUri = array();
private $_postController = array();
private $_postMethod = array();
public function __construct()
{
}
/**
* Build a collection of internal GET URLs to look for
* #param $uri - The url that the user types in the browser
* #param $controller - The controller that will handle the url
* #param $method - The method of the controller that will run
*/
public function get($uri, $controller, $method)
{
$this->_getUri[] = $uri;
$this->_getController[] = $controller;
$this->_getMethod[] = $method;
}
/**
* Build a collection of internal POST URLs to look for
* #param $uri - The url that the user types in the browser
* #param $controller - The controller that will handle the url
* #param $method - The method of the controller that will run
*/
public function post($uri, $controller, $method)
{
$this->_postUri[] = $uri;
$this->_postController[] = $controller;
$this->_postMethod[] = $method;
}
public function submit()
{
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
var_dump($_SERVER['REQUEST_URI']);
echo "\n";
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); //get the url
var_dump($path);
foreach ($this->_getUri as $key => $value)
{
if (preg_match("#^$value$#", $path))
{
// echo $key . ' => ' . $value; //See what the $path returns
//Instantiate Controller
$controller = 'Fab\Controllers\\' . $this->_getController[$key];
$controller = new $controller();
//Call the appropriate method
$method = $this->_getMethod[$key];
$controller->$method();
}
}
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); //get the url
foreach ($this->_postUri as $key => $value)
{
if (preg_match("#^$value$#", $path))
{
//echo $key . ' => ' . $value; //See what the $path returns
//Instantiate Controller
$controller = 'Fab\Controllers\\' . $this->_postController[$key];
$controller = new $controller();
//Call the appropriate method
$method = $this->_postMethod[$key];
$controller->$method();
}
}
}
}
}
This allows me to say in my index.php something like:
$router->get('/portfolio', 'MainController', 'portfolio');
What I want to do now is to create an individual page for every item in the portfolio and give it a unique URL.
For example, if I have the item 'dinosaur' in my portfolio, I want to be able to say:
$router->get('/portfolio/dinosaur', 'MainController', 'searchIfDinosaurExistsInDatabase');
So generally, I would like to have in my index sth like:
$router->get('/portfolio/{item}', 'MainController', 'searchInDbForItem');
How can I modify my router in order to achieve this?
You need to add an regular expression to route. For example like this (it's a very simple example):
$router->get('/portfolio/[\w\d]+', 'MainController', 'searchInDbForItem');`
And you need use preg_match for comparison a route and an url.
It's a very simple example of router https://github.com/newage/example/blob/master/core/Route/HttpRoute.php
I'm developing a WebApp which (as usual) must support customer specific functionalities.
To achieve it I plan to set the customer name in the local app configuration (config/autoload/local.php )
configuration file so that I can use it to call the specialized code later on.
The module folder structure is this:
/module/Application
/module/Application/config
/module/Application/src
/module/Application/src/Application
/module/Application/src/Application/Controller
/module/Application/src/Application/Controller/[customer_instance_name]
/module/Application/src/Application/Model
/module/Application/src/Application/Model/[customer_instance_name]
/module/Application/view
/module/Application/view/Application
/module/Application/view/Application/[action]
/module/Application/view/Application/[action]/[customer_instance_name]
Using a custom ViewModel I inject the specific customer name to the template path:
namespace Application\Model;
use Zend\View\Model\ViewModel;
use Zend\View\Resolver\TemplatePathStack;
use Zend\Mvc\Service\ViewTemplatePathStackFactory;
class MyViewModel extends ViewModel
{
private $customInstanceName;
private $pathStack;
/**
* Constructor
*
* #param null|array|Traversable $variables
* #param array|Traversable $options
*/
public function __construct($variables = null, $options = null)
{
parent::__construct ( $variables, $options );
$serviceLocator = MySingleton::instance()->serviceLocator;
$factory = new ViewTemplatePathStackFactory();
$this->pathStack = $factory->createService($serviceLocator);
$config = $serviceLocator->get('config');
if (isset($config['custom_instance_name']) AND ($config['custom_instance_name']!='')) {
$this->customInstanceName = $config['custom_instance_name'];
} else {
$this->customInstanceName = false;
}
}
/**
* Set the template to be used by this model
*
* #param string $template
* #return ViewModel
*/
public function setTemplate($template)
{
$this->template = (string) $template;
if ( $this->customInstanceName === false) {
return $this;
}
$pathComponents = explode('/', (string) $template);
$last = array_pop($pathComponents);
array_push($pathComponents, $this->customInstanceName);
array_push($pathComponents, $last);
$customTemplate = implode('/', $pathComponents);
if ($this->pathStack->resolve($customTemplate) !== false) {
$this->template = $customTemplate;
}
return $this;
}
}
Using the "Decorator Pattern" I can achieve the same customization level on my Models.
I'm having problem to handle specific behavior. In this case I plan to create custom Controllers extending
my base controller class, but I'unable to call those controllers since the routing is defined on the module
config (and I was unable to change it in runtime).
My questions are:
1) Is this approach correct, or there is a better way to do it?
2) If the approach is correct, how can I define a custom router to be used when the ServiceManager reads my routing config?
Just found a solution. Will register it here hoping someone will benefit from it.
All I had to do was to create a specific router class with a match method which returns the correct routing target for each customer controller, and add it to my module.config.php as the type for each action.
namespace TARGETNAMESPACE;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Mvc\Router\Http\RouteInterface;
use Zend\Mvc\Router\Http\RouteMatch;
use Zend\Mvc\Router\Http\Literal;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
class MyRouterLiteral extends Literal {
public function match(Request $request, $pathOffset = null) {
if (! method_exists($request, 'getUri')) {
return null;
}
$uri = $request->getUri();
$path = $uri->getPath();
if ($pathOffset !== null) {
if ($pathOffset >= 0 && strlen($path) >= $pathOffset && ! empty($this->route)) {
if (strpos($path, $this->route, $pathOffset) === $pathOffset) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
}
return null;
}
if ($path === $this->route) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
return null;
}
private function getDefaults() {
$aux = explode('\\', $this->defaults['controller']);
$last = array_pop($aux);
array_push($aux, '[CUSTOM_INSTANCE_NAME]');
array_push($aux, '[CUSTOM_INSTANCE_NAME]'.$last);
$result = $this->defaults;
$result['controller'] = implode('\\', $aux);
return $result;
}
}
To address all cases I had to create a second custom router (for segment routes) which follows the same rules and can be easily derived from the above code.
I'm building a simple CMS module for my ZendFramework application.
Currently I have all my routes in a .ini file, would it be possible to make it db-driven. Or if it's possible to build a fallback method to a DB-check if the route doesn't exists in the .ini file.
UPDATE - SOLUTION
Hope this will help somebody - Please note that I have custom My_Db_table and My_Db_Row, which I haven't posted here. So ->fetchAllActive() probably needs change to ->fetchAll(); and the getters on the row object may/may-not work, can't remember if they are custom - you'll figure it out ;-)
But Router basically check against DB if none of the other routes apply, i.e. from a .ini file. I haven't checked if this works with all the route types, but it works with the default route type. I use DB routes to point urls down to a pageController with additional params like pageId stored as a JSON string in the route_defaults cell. But you can basically use this for all kind of routes.
In bootstrap
function _initApplication ()
{
// Something taken out for simplicity
$this->bootstrap('frontcontroller');
$front = $this->getResource('frontcontroller');
$router = new My_Controller_Router_Rewrite();
$front->setRouter($router);
// Something taken out for simplicity
}
My/Controller/Router/Rewrite.php
<?php
class My_Controller_Router_Rewrite extends Zend_Controller_Router_Rewrite
{
/**
* Retrieve a named route
*
* #param string $name Name of the route
* #throws Zend_Controller_Router_Exception
* #return Zend_Controller_Router_Route_Interface Route object
*/
public function getRoute($name)
{
if (!isset($this->_routes[$name])) {
/* BEGIN - DB routes */
$routes = new Routes();
$route = $routes->getNamedRoute($name);
if($route instanceof Zend_Controller_Router_Route_Abstract) {
$this->addRoute($name, $route);
}
/* END - DB routes */
if (!isset($this->_routes[$name])) {
require_once 'Zend/Controller/Router/Exception.php';
throw new Zend_Controller_Router_Exception("Route $name is not defined");
}
}
return $this->_routes[$name];
}
/**
* Find a matching route to the current PATH_INFO and inject
* returning values to the Request object.
*
* #throws Zend_Controller_Router_Exception
* #return Zend_Controller_Request_Abstract Request object
*/
public function route(Zend_Controller_Request_Abstract $request)
{
if (!$request instanceof Zend_Controller_Request_Http) {
require_once 'Zend/Controller/Router/Exception.php';
throw new Zend_Controller_Router_Exception('Zend_Controller_Router_Rewrite requires a Zend_Controller_Request_Http-based request object');
}
if ($this->_useDefaultRoutes) {
$this->addDefaultRoutes();
}
// Find the matching route
$routeMatched = false;
foreach (array_reverse($this->_routes, true) as $name => $route) {
// TODO: Should be an interface method. Hack for 1.0 BC
if (method_exists($route, 'isAbstract') && $route->isAbstract()) {
continue;
}
// TODO: Should be an interface method. Hack for 1.0 BC
if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) {
$match = $request->getPathInfo();
} else {
$match = $request;
}
if ($params = $route->match($match)) {
$this->_setRequestParams($request, $params);
$this->_currentRoute = $name;
$routeMatched = true;
break;
}
}
/* BEGIN - DB routes */
$front = Zend_Controller_Front::getInstance();
if (!$routeMatched || ($routeMatched && !Zend_Controller_Front::getInstance()->getDispatcher()->isDispatchable($request))) {
$routes = new Routes();
$dbRoutes = $routes->getRouterRoutes();
foreach ($dbRoutes as $name => $route) {
// TODO: Should be an interface method. Hack for 1.0 BC
if (method_exists($route, 'isAbstract') && $route->isAbstract()) {
continue;
}
// TODO: Should be an interface method. Hack for 1.0 BC
if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) {
$match = $request->getPathInfo();
} else {
$match = $request;
}
if ($params = $route->match($match)) {
$this->_setRequestParams($request, $params);
$this->_currentRoute = $name;
$routeMatched = true;
break;
}
}
}
/* END - DB routes */
if(!$routeMatched) {
require_once 'Zend/Controller/Router/Exception.php';
throw new Zend_Controller_Router_Exception('No route matched the request', 404);
}
if($this->_useCurrentParamsAsGlobal) {
$params = $request->getParams();
foreach($params as $param => $value) {
$this->setGlobalParam($param, $value);
}
}
return $request;
}
}
Routes Db_Table model
<?php
class Routes extends My_Db_Table
{
protected $_name = 'routes';
protected $_rowClass = 'Route';
public static $primaryColumn = 'route_id';
public static $statusColumn = 'route_status';
public static $nameColumn = 'route_name';
public static $typeColumn = 'route_type';
public static $urlColumn = 'route_url';
public static $moduleColumn = 'route_module';
public static $controllerColumnn = 'route_controller';
public static $actionColumnn = 'route_action';
public static $defaultsColumnn = 'route_defaults';
public static $reqsColumnn = 'route_reqs';
public static $createdColumnn = 'route_created';
public function getRouterRoutes() {
$routes = array();
$rowset = $this->fetchAllActive();
foreach($rowset as $row) {
$routes[$row->getName()] = $row->getRouteObject();
}
return $routes;
}
public function getNamedRoute($name) {
$select = $this->select()
->where(self::$statusColumn . ' = ?', 1)
->where(self::$nameColumn . ' = ?', $name);
$rowset = $this->fetchAll($select);
foreach($rowset as $row) {
return $row->getRouteObject();
}
}
}
Route - Db_Table_row
<?php
class Route extends My_Db_Table_Row_Observable
{
public function getType() {
if(empty($this->{Routes::$typeColumn})) {
return "Zend_Controller_Router_Route";
} else {
return $this->{Routes::$typeColumn};
}
}
public function getDefaults() {
$defaults = $this->{Routes::$defaultsColumnn};
if($defaults) {
$defaults = Zend_Json::decode($defaults);
} else {
$defaults = array();
}
$defaults['module'] = $this->getModule();
$defaults['controller'] = $this->getController();
$defaults['action'] = $this->getAction();
return $defaults;
}
public function getReqs() {
$reqs = $this->{Routes::$reqsColumnn};
if($reqs) {
$reqs = Zend_Json::decode($reqs);
} else {
$reqs = array();
}
return $reqs;
}
public function getModule() {
if(empty($this->{Routes::$moduleColumn})) {
return "default";
} else {
return $this->{Routes::$moduleColumn};
}
}
public function getController() {
if(empty($this->{Routes::$controllerColumnn})) {
return "default";
} else {
return $this->{Routes::$controllerColumnn};
}
}
public function getAction() {
if(empty($this->{Routes::$actionColumnn})) {
return "default";
} else {
return $this->{Routes::$actionColumnn};
}
}
public function getRouteObject() {
$class = $this->getType();
$defaults = $this->getDefaults();
$reqs = $this->getReqs();
$route = new $class($this->getUrl(), $defaults, $reqs);
return $route;
}
}
SQL schema for routes table
CREATE TABLE IF NOT EXISTS `routes` (
`route_id` smallint(1) unsigned NOT NULL AUTO_INCREMENT,
`route_status` tinyint(1) unsigned NOT NULL,
`route_name` varchar(16) NOT NULL,
`route_type` varchar(255) NOT NULL,
`route_url` varchar(255) NOT NULL,
`route_module` varchar(32) NOT NULL,
`route_controller` varchar(32) NOT NULL,
`route_action` varchar(32) NOT NULL,
`route_defaults` varchar(255) NOT NULL,
`route_reqs` varchar(255) NOT NULL,
`route_created` datetime NOT NULL,
PRIMARY KEY (`route_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
There are three approaches that I can think of.
First is what I've successfully used in ZF1. Here's the code for the routing itself. The idea is simple: set an "alias" routing (which, in my case, simply used the ".html" suffix to differentiate it from other URLs). If the routing is found, you can get the alias from the DB and then forward the request from the target controller+action to what's defined in the DB (like here). My code isn't pretty, but it works.
Second: write your own router. You can extend the router class and simply add yout own route resolving rules there: get data from the DB, return true (and set params) if the alias is in the database.
Third: scrape the aliases table and store everything in an .ini file (or whatever cache you might be using). This is not much different from what you've implemented already and all you'd need to do would be to automate the alias scraping.
I'm trying out the Slim php framework
Is it possible to have layouts or sub views in Slim? I'd like to use a view file as a template with variables as placeholders for other views loaded separately.
How would I do that?
filename: myview.php
<?php
class myview extends Slim_View
{
static protected $_layout = NULL;
public static function set_layout($layout=NULL)
{
self::$_layout = $layout;
}
public function render( $template ) {
extract($this->data);
$templatePath = $this->getTemplatesDirectory() . '/' . ltrim($template, '/');
if ( !file_exists($templatePath) ) {
throw new RuntimeException('View cannot render template `' . $templatePath . '`. Template does not exist.');
}
ob_start();
require $templatePath;
$html = ob_get_clean();
return $this->_render_layout($html);
}
public function _render_layout($_html)
{
if(self::$_layout !== NULL)
{
$layout_path = $this->getTemplatesDirectory() . '/' . ltrim(self::$_layout, '/');
if ( !file_exists($layout_path) ) {
throw new RuntimeException('View cannot render layout `' . $layout_path . '`. Layout does not exist.');
}
ob_start();
require $layout_path;
$_html = ob_get_clean();
}
return $_html;
}
}
?>
example index.php:
<?php
require 'Slim/Slim.php';
require 'myview.php';
// instantiate my custom view
$myview = new myview();
// seems you need to specify during construction
$app = new Slim(array('view' => $myview));
// specify the a default layout
myview::set_layout('default_layout.php');
$app->get('/', function() use ($app) {
// you can override the layout for a particular route
// myview::set_layout('index_layout.php');
$app->render('index.php',array());
});
$app->run();
?>
default_layout.php:
<html>
<head>
<title>My Title</title>
</head>
<body>
<!-- $_html contains the output from the view -->
<?= $_html ?>
</body>
</html>
The slim framework makes use of other templating engines such as Twig or Smarty so as long as you choose a templating engine that allows subviews, then it'll work. For more info on view templating in Slim, check here.
I'm working with my View:
class View extends \Slim\View
{
protected $layout;
public function setLayout($layout)
{
$this->layout = $layout;
}
public function render($template)
{
if ($this->layout){
$content = parent::render($template);
$this->setData(array('content' => $content));
return parent::render($this->layout);
} else {
return parent::render($template);
}
}
}
You may add some method like setLayoutData(..) or appendLayoutData(..)
Few minutes ago:
class View extends \Slim\View
{
/** #var string */
protected $layout;
/** #var array */
protected $layoutData = array();
/**
* #param string $layout Pathname of layout script
*/
public function setLayout($layout)
{
$this->layout = $layout;
}
/**
* #param array $data
* #throws \InvalidArgumentException
*/
public function setLayoutData($data)
{
if (!is_array($data)) {
throw new \InvalidArgumentException('Cannot append view data. Expected array argument.');
}
$this->layoutData = $data;
}
/**
* #param array $data
* #throws \InvalidArgumentException
*/
public function appendLayoutData($data)
{
if (!is_array($data)) {
throw new \InvalidArgumentException('Cannot append view data. Expected array argument.');
}
$this->layoutData = array_merge($this->layoutData, $data);
}
/**
* Render template
*
* #param string $template Pathname of template file relative to templates directory
* #return string
*/
public function render($template)
{
if ($this->layout){
$content = parent::render($template);
$this->appendLayoutData(array('content' => $content));
$this->data = $this->layoutData;
$template = $this->layout;
$this->layout = null; // allows correct partial render in view, like "<?php echo $this->render('path to parial view script'); ?>"
return parent::render($template);;
} else {
return parent::render($template);
}
}
}
With Slim 3
namespace Slim;
use Psr\Http\Message\ResponseInterface;
class View extends Views\PhpRenderer
{
protected $layout;
public function setLayout($layout)
{
$this->layout = $layout;
}
public function render(ResponseInterface $response, $template, array $data = [])
{
if ($this->layout){
$viewOutput = $this->fetch($template, $data);
$layoutOutput = $this->fetch($this->layout, array('content' => $viewOutput));
$response->getBody()->write($layoutOutput);
} else {
$output = parent::render($response, $template, $data);
$response->getBody()->write($output);
}
return $response;
}
}
In layout:
<?=$data['content'];?>
You can use this in your parent view:
$app = \Slim\Slim::getInstance();
$app->render('subview.php');
If you want to capture the output in a variable, it's as easy as using the view's fetch() method:
//assuming $app is an instance of \Slim\Slim
$app->view()->fetch( 'my_template.php', array( 'key' => $value ) );
Yes it is possible. If you do not want to use any template engine like smarty or twig. If you want to use only .php view file. Follow these steps.
Step 1. Make a new directory in your project's root directory. e.g templates.
Step 2. Open dependencies.php and paste below code
$container = $sh_app->getContainer();
$container['view'] = function ($c) {
$view = new Slim\Views\PhpRenderer('src/templates/');
return $view;
};
Step 3. In your controller's method for rendering view code should be looking like this.
$this->container->view->render($response,'students/index.php',['students' => $data]);