So after fixing mustache and Apache perms I now have ran into an issue where the site will load the index page, but any page after that located in (the same place a index.html) /pages as it fails to load.
What you can see below is the index.php for Slim to do it's stuff. I can't really understand why the index page will load just fine but no other page will load. If you can point me in the right direction then it would be greatly appreciated.
Examples:
End User > myexample.com/
Location > myexample.com/pages/index.html
The above works just fine but if you look below, you should be able to understand my issue.
End User > myexample.com/info
Location > myexample.com/pages/info.html
The End User is what you see on the site, but the location is where the files belong.
Thanks in advance, Luke.
<?php
// Load up composer autoload, and instantiate the application.
require 'vendor/autoload.php';
$app = new \Slim\Slim;
// Register a singleton of the Mustache engine, and tell it to cache
$app->container->singleton('mustache', function () {
return new Mustache_Engine(array(
'cache' => 'storage',
'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__) . '/pages', array('extension' => '.html'))
));
});
// Helper function to render templates
function renderTemplate($name, $data = array()) {
global $app;
if (file_exists('config.php')) {
$data = (require 'config.php');
}
$data += array(
'resourceUri' => $app->request->getResourceUri() ?: 'index',
'request' => $app->request
);
return $app->mustache->loadTemplate($name)->render($data);
}
// Loads a page by the given name/path
function loadPage($path) {
global $app;
// Set up the base path
$f = 'pages/' . $path;
if (file_exists($f . '.html')) {
// If there's an HTML file, render the mustache template
return renderTemplate($path . '.html');
} elseif (file_exists($f . '.php')) {
// If there's a PHP file, return it
return (require $f . '.php');
} elseif ($path != '404') {
// Otherwise, go get the 404 page
return loadPage('404');
} else {
// But if the user doesn't have a 404 page made, return a plain 404
$app->halt(404);
}
}
// Index page
$app->get('/', function () use ($app) {
echo loadPage('index');
});
// Route to everything else
$app->get('/.*?', function () use ($app) {
// Get the current request path
$path = $app->request->getResourceUri();
// Make sure the user isn't trying to escape and do nasty things
if (!preg_match('/^[A-z\/\-\_]+$/', $path)) {
echo loadPage('404');
}
// Send the page off to get loaded
echo loadPage($path);
});
$app->run();
I think you'll find your problem isn't with Slim but rather with the custom functions you wrote to allow Slim to load Mustache templates.
I would highly recommend removing those functions and using the custom Slim Mustache view provided in the views directory of Slim-Extras repo. At that point, any issues with Slim can be more easily diagnosed as there won't be custom functions to debug.
NOTE: While the Slim-Extras repo is deprecated in favor of the Slim-Views repo, the custom Mustache view (which hasn't made it to the Slim-Views repo) should work fine.
For reference please see the Custom Views section of the Slim documentation.
UPDATE: I've been using Dearon's Slim Mustache library for integration with Slim View in a new app of mine and Justin Hileman's PHP implementation of Mustache. Both are highly recommended and simple to install and configure. Good luck!
Related
I have an old project I'm working on using Slim version 2. I can not upgrade to 3.
I'm trying to integrate twig into slim 2 while also keeping the old default slim2 renderer.
Currently I have this.
class TwigView extends \Slim\View
{
public function rendertwig($template,$data = array()){
global $twig;
$twigResults = $twig->render($template,array('test' => '1'));
$data = array_merge($this->data->all(), $data);
return $this->render($twigResults, $data);
}
}
$view = new TwigView();
$config['view'] = $view; //#JA - This command overides the default render method.
//#JA - Intialize Slim
$app = new \Slim\Slim($config);
The idea is that I would call this saying $app->view->rendertwig('file.twig') when I need to render the twig templates and use $app->render('template.php') for all the other templates that use the default slim2 method of templating.
However, I get an error because in my rendertwig function $this->render() function requires a template name for the first parameter. Is there a way I can render directly the results from twig into the slim engine without needing a template file?
I'm aware this is bad form to have two templating engines but eventually I will switch everything to Twig but I need this as a temporary solution till I can patch everything over.
When I inspected slim's view object it has this defined as its render method which will explain the issue.
protected function render($template, $data = null)
{
$templatePathname = $this->getTemplatePathname($template);
if (!is_file($templatePathname)) {
throw new \RuntimeException("View cannot render `$template` because the template does not exist");
}
$data = array_merge($this->data->all(), (array) $data);
extract($data);
ob_start();
require $templatePathname;
return ob_get_clean();
}
I don't know if this is bad form but I did this as a temporary solution.
class TwigView extends \Slim\View
{
public function rendertwig($template,$data = array()){
global $twig;
$twigResults = $twig->render($template,array('test' => '1'));
echo $twigResults;
}
}
I saw that all the render method did was just require the template so I figured its safe to just echo the results from the twig templating engine? This seemed to work from my test.
For example in a Yii Framework application the url is in this format
www.example.com/index.php?r=foo/bar
Which renders the script inside the actionBar() method of class FooController. Further, this class (or its parent class) implements a render() method which can render a view file.
All the url's are handled through the entry script index.php.
I would like to write my own class which can handle url's through this way.
Can someone give me a very basic 'hello world' example of writing such a script ?
I'll give it a shot:
// index.php
$r = $_REQUEST['r']; // 'foo/bar'
$rParts = explode('/',$r);
$foo = $rParts[0];
$bar = $rParts[1];
$controller = new $foo; // foo
echo $controller->$bar();
Here is what I did for a friend recently, when teaching him how frameworks works. This is a basic example, but it demonstrates how a container works, how to handle the router, giving the controller a request and a response and handling redirects and the like.
<?php
require 'autoload.php';
$container = [];
$container['controller.elephant'] = function() {
return new Controller\Elephant();
};
$routes = [];
$routes['/babar'] = 'controller.elephant:babar';
$routes['/celeste'] = 'controller.elephant:celeste';
$request = new Request();
if (!isset($routes[$request->path()])) {
http_response_code(404);
exit;
}
$route = $routes[$request->path()];
list($class, $method) = explode(':', $route);
$controller = $container[$class]();
$response = $controller->{$method}($request, new Response());
if ($response->isRedirect()) {
http_response_code($response->status());
header('Location: '.$response->destination());
} else {
echo $response->content();
}
exit;
I won't include anything more than that (albeit there is other files) because it would bloat the answer needlessly (I can send them to you by other means if you want to).
I highly advise you to look at the Slim Framework code, as it is a micro framework that basically do just that.
Well in the Symfony documentation you have this page: http://symfony.com/doc/current/components/http_kernel/introduction.html
where it explains how is the life cycle of a request, it's just a flow diagram.
But it will give you a really good idea on how you should build yours
If you are more interested in how based on a url you get the controller you should read the RoutingComponent in symfony
http://symfony.com/doc/current/components/routing/introduction.html
http://symfony.com/doc/current/components/routing/hostname_pattern.html
But if you want to write your own class, you should use something like regex expression groups where you can detect the url parts separated by i.e: '/' then you somehow map the url to the controller i.e associative array 'Hash'
someurl.com/someController/someAction
$mappings = [
...
'someController' => 'The\Controller\Class'
]
$controller = new $mappings[$urlControllerPart]();
$response = $controller->{$urlActionPart}($request);
return $response;
I'm using Codeigniter + Grocery Crud.
So, GC working good but is blank.. shows results from database, but search option and css style doesn't load.
My structure is:
application
assets
system
GC is inside assets.
My controller load view and send output to view:
public function output($output = null) {
$this->load->view('welcome_message', $output);
}
public function users() {
$this->load->library('grocery_CRUD');
$this->output((object) array('output' => '', 'js_files' => array(), 'css_files' => array()));
try {
$crud = new grocery_CRUD();
$crud->set_theme('flexigrid');
$crud->set_table('suppliers');
$output = $crud->render();
$this->output($output);
} catch (Exception $e) {
show_error($e->getMessage() . ' --- ' . $e->getTraceAsString());
}
}
After that, i'm using CG official tutorial to include jQuery and CSS files in the view. They are there. When i click over (in view source) - i can see them. It's not problem in folders.
I saw in View source double html tags... Is it exist any codeigniter autoload library to do that?
I'm defined autoload helpers - url and utility, which i made for loading assets folder.
I'm supposing the problem is ajax, jquery...but how to fix it?
Grocery CRUD output contains an array for adding java-script files
If you want to add extra/custom js file then you have to do something like this:
$output = $crud->render();
array_push($output->js_files, base_url("assets/my_js_folder/myjs.file.js"));
array_push($output->js_files, base_url("assets/another_js/config.module.js"));
$this->output($output);
I really hope this help you :)
I am building an AJAX web app, using PHP for my back end. I am trying to design a routing system that will let me easily drop new pages in, and let me focus on the Javascript. The actual pages that PHP will be serving up are simple, just views that are essentially containers for Javascript charts (built with d3.js). Thus, my controller won't even have to interact with my model until I start making AJAX calls.
I am new to OOP, especially in back end. I've been doing a bit with Javascript, but I am brand new to incorporating OOP with MVC & solving the issue of routing. I know there are modules/plugins out there that have Routing classes written, but as the back end part of this project is very straight-forward - essentially, how best to serve up an 'About' page on a blog - I'd like to take this opportunity to learn it thoroughly myself.
I have one controller:
<?php
//controller.php
include 'views/view.php';
class Controller
{
public function homeAction() {
$view = new View();
$view->setTemplate('views/home.php');
$view->render();
}
public function categoryAction($category) {
$view = new View();
$view->setTemplate("views/Monitor/{$category}/{$category}.php");
$view->setCategory($category);
$view->render();
}
public function monitorAction($category, $monitor) {
$view = new View();
$view->setTemplate("views/Monitor/{$category}/{$monitor}.php");
$view->setCategory($category);
$view->setMonitor($monitor);
$view->render();
}
}
?>
Right now, I instantiate my controller at the beginning of index.php:
<?php
// Load libraries
require_once 'model.php';
require_once 'controller.php';
$controller = new Controller();
$uri = str_replace('?'.$_SERVER['QUERY_STRING'], '', $_SERVER['REQUEST_URI']);
// home action
if ($uri == '/') {
$controller->homeAction();
// /{category}/{monitor}
} elseif (preg_match("#/(.+)/(.+)#", $uri, $matches) ) {
$category = $matches[1];
$monitor = $matches[2];
$controller->monitorAction($category, $monitor);
// /{category}
} elseif (preg_match("#/([^/.]+)#", $uri, $matches) ) {
$category = $matches[1];
$controller->categoryAction($category);
// 404
} else {
header('Status: 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && (!empty($_GET)) && $_GET['action'] == 'get_data') {
$function = $_GET['chart'] . "_data";
$dataJSON = call_user_func($function);
header('Content-type: application/json');
echo $dataJSON;
}
?>
I have read a bit about PHP's autoloader, but I'd like to get it down manually first, because I want to make sure and understand the fundamentals.
Is this the appropriate place to instantiate my Controller object?
First, your architecture is facing some major problems. You need a router to take care of your requested URIs by the users and next you need an initialization state for your system. The usual way to create Controllers is to extend a parent class, then in your parent class __construct method you can initialize your children controllers, however, your system isn't in a good shape.
This is a gold link that I never delete:
http://johnsquibb.com/tutorials/mvc-framework-in-1-hour-part-one
dir:
application
-controllers
-models
-views
-mobile_views
How do I auto load templates at mobile_views when I use $this->load->view and view by iphone or other mobile phone?
Check this
You can do it in two way.
Way 1: Its very simple. In the above answer (the link I have given) add following line in the end of MyController function
$this->load->_ci_view_path . = $this->view_type .'/';
You are done. You can simply load view like normal view load.
Way 2:
To autoload a view based on user agent, I think you can implement it using hooks. To implement this hooks you need to follow the following steps
Autoload user agent library in autoload.php
$autoload['libraries'] = array('user_agent');
Enable hooks in config.php
$config['enable_hooks'] = TRUE;
Not implement hooks on post_controller_constructor. Add following codes to hooks.php
$hook['post_controller_constructor'][] = array('class' => 'Loadview',
'function' => 'load',
'filename' => 'loadview.php',
'filepath' => 'hooks'
);
Now create a page named loadview.php under hooks directory having following code
class Loadview
{
public static $MOBILE_PLATFORM = 'mobile';
public static $DEFAULT_PLATFORM = 'default';
public function load(){
$this->CI =& get_instance();
$view_type = $this->CI->agent->is_mobile() ? self::$MOBILE_PLATFORM : self::$DEFAULT_PLATFORM;
$this->CI->load->_ci_view_path = $this->CI->load->_ci_view_path . $view_type .'/';
}
}
You are done now. You can simply load view like normal view load.
to load views from another dir aside from "views", i found this forum topic to be helpful
http://codeigniter.com/forums/viewthread/132960/
function external_view($path, $view, $vars = array(), $return = FALSE)
{
$full_path = $path.$view.'.php';
if (file_exists($full_path))
{
return $this->_ci_load(array('_ci_path' => $full_path, '_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
}
else
{
show_error('Unable to load the requested module template file: '.$view);
}
}
and you can work the rest from the controller.
I do this in my controller:
public function index()
{
if($this->agent->is_mobile())
{
$this->load_mobile();
}
else
{
$this->load_web();
}
}
public function load_mobile()
{
$this->load->view('mobile/home');
}
public function load_web()
{
$this->load->view('web/home');
}
In this way I can add different data to mobile and to web pages.
I also extend the default controller and add some useful extra features:
Enables the usage of master page/templates.
Can add css and javascript files.
Uses the _output method for controlling the controllers output.
Can load relative content with in the form of modules (views)
So I can manage better the different pages.
Bye!!