I´m making a tiny MVC framework for learning purpose.
I have a file and a class called load which is in my model folder. It reads the requested file name and checks if the file is in the views folder and returns the correct file.
The problem I´m having is that I´m trying to make a handling for bad urls so that if the requested file/url does not excist you are directed to the index.php page in the views folder...
I have an if statement inside the function that checks if the file exists and I thought I could just write an else statement requiring the index.php file incase the file was not found...
But this doesn´t work. All I´m getting is a white blank page when I type in an non existing page even I´f I try echo something in the else statement...
Does anyone know what´s missing or what I´m doing wrong?
UPDATE:
Added mainController class
This is what the hole load class looks like:
<?php
/* model/load.php
*/
class load
{
/* This function takes parameter
* $file_name and match with file in views.
*/
function view($file_name, $data = null)
{
if (is_readable('views/' . $file_name)) {
if (is_array($data)) {
extract($data);
}
require 'views/' . $file_name;
} else {
//This is where I thought I could require the index.php file...
}
}
}
And in my controller folder I have a mainController class sending the files to the load file.
This is what the mainController class looks like:
<?php
/* controller/main.php
*
*/
class mainController
{
public $load;
public $urlValues;
public function __construct()
{
$url = parse_url($_SERVER['REQUEST_URI']);
$url = explode('/', trim($url['path'], '/'));
$this->urlValues = array('controller' => $url[1]);
//Index page
if ($this->urlValues['controller'] == "index.php") {
$key = array("key" => "");
$this->load = new load();
$this->load->view('index.php', $key);
}
//Register page
if ($this->urlValues['controller'] == "register.php") {
$this->load = new load();
$this->load->view('register.php');
}
//Home page
if ($this->urlValues['controller'] == "home.php") {
$this->load = new load();
$this->load->view('home.php');
}
}
}
It looks as though you are only calling the view method when the controller matches, and therefore it never gets executed. Try something like this, and work from there:
<?php
/* controller/main.php
*
*/
class mainController
{
public $load;
public $urlValues;
public function __construct()
{
$url = parse_url($_SERVER['REQUEST_URI']);
$url = explode('/', trim($url['path'], '/'));
$this->urlValues = array('controller' => $url[1]);
// ifs go here
$this->load = new load();
$this->load->view($this->urlValues['controller']);
}
}
Related
This question is related to this question
Loading view outside view folder with CodeIgniter
But the problem it is very old, and nobody looks it any more :(
This is problem i have, i have created function based on #SpYk3HH answer like this
MY_Loader.php
<?php
class MY_Loader extends CI_Loader {
public function base_view($view, $vars = array(), $get = FALSE) {
// ensures leading /
if ($view[0] != '/') $view = '/' . $view;
// ensures extension
$view .= ((strpos($view, ".", strlen($view)-5) === FALSE) ? '.php' : '');
// replaces \'s with /'s
$view = str_replace('\\', '/', $view);
if (!is_file($view)) if (is_file($_SERVER['DOCUMENT_ROOT'].$view)) $view = ($_SERVER['DOCUMENT_ROOT'].$view);
if (is_file($view)) {
if (!empty($vars)) extract($vars);
ob_start();
include($view);
$return = ob_get_clean();
if (!$get) echo($return);
return $return;
}
return show_404($view);
}
}
In controller I have used it like this
Welcome.php
class Welcome extends CI_Controller {
function __construct()
{
parent::__construct();
// Load globals
}
/**
* Index Page for this controller
*/
public function index()
{
$data['lang'] = '1';
$data['body_render']='mypages/home.php';
$this->load->view("/layouts/view_layout", $data);
}
view_layout.php
<?php $this->load->base_view($body_render); ?>
home.php
<?php echo $lang; ?>
But i got error, like i can not pass $lang to that partial inside partial?
Message: Undefined variable: lang
I modified all my contollers and views to very simple that somebody can understand.
As far as I can see in your codes, you didn't pass any variable to MY_Loader::base_view() when you are calling it
in your view_layout.php you need to pass variables you need
for example:
<?php $this->load->base_view($body_render, ['lang'=> $lang]); ?>
This is my parent class code: \core\controller\controlmaster
<?php
namespace core\controller;
class controlmaster{
private $model_base = null;
//public function __construct(){
//always start session for any loaded controller
//session_start();
//}
public function _loadModels($models){
$file = dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR.$this->model_base.$models.".php";
$file_alloc = str_replace("\\","/",$file);
if(file_exists($file_alloc)){
require($file_alloc);
$models_name = $this->model_base.$models;
return new $models_name();
}
}
public static function _setModelBase($model_location){
$this->model_base = $model_location;
}
}
?>
and this is my controller page for application : \applications\controller\index
<?php
namespace applications\controllers;
use core\configuration\configloader as config;
class index extends \core\controller\controlmaster{
public $config;
public function __construct(){
$this->config = new config;
$this->config->_parsePHP("j3mp_setting.php");
parent::_setModelBase($this->config->settings['app_model_base']); // Error doesn't appear when i comment this function
echo "This is main page and this is config for model app base : {$this->config->settings['base_url']}";
}
}
?>
This is core\configuration\configloader:
<?php
namespace core\configuration;
class configloader{
//Contains setting informations
public $inisettings;
public $settings;
public function _parseINI($file,$section){
$section = empty($section) ? false : true;
$file = __DIR__.DIRECTORY_SEPARATOR.$file;
if(file_exists($file)){
$parse = parse_ini_file($file,$section);
$this->inisettings = $parse;
}else{
throw new core\errorhandler\exception("File {$file} is not found in our system. Please contact administrator for details.","configloader");
}
}
public function _parsePHP($file){
$file = __DIR__.DIRECTORY_SEPARATOR.$file;
if(file_exists($file)){
$settings = array();
include($file);
$this->settings = $settings;
}else{
throw new core\errorhandler\exception("File {$file} is not found in our system. Please contact administrator for details.","configloader");
}
}
}
?>
When i comment "parent::_setModelBase(...)" code, the error disappear and the browser successfully print "This is main page and this is config for model app base : http://iosv3.net/". I think the error come from \core\controller\controlmaster, but I don't know how to fix that? I always try to edit the file (\core\controller\controlmaster) but notting happens... If error occured, the message ("This is main page ...") doesn't come out... Could you show me where is the error come from? Thanks...
I have created a library that which will load a php file (which may contains users custom functions in it...) you can either call it from bootstrap also from the controller. if file does not exists it will display the error msg. Thing is am i doing it in the currect way?
If i did miss any thing point me out.. Thanks
Helpers is the folder where users can put php files
app/
controllers/
models/
helpers/
library/
views/
In "library/" Folder a php file named "helperfile.php"
class helperfile extends Phalcon\Mvc\User\Component
{
var $helper_Folder = '../app/helpers';
var $files = array();
public function __construct()
{
}
public function initialize()
{
}
public function include_file($files, $run = true)
{
if (!is_array($files))
$files = array($files);
foreach ($files as $file)
$this->files[$file] = $file;
if ($run)
$this->load();
}
public function beforeDispatch()
{
$this->load();
}
private function load()
{
if (empty($this->files))
return false;
foreach ($this->files as $file) {
$file = trim($file) . '.php';
if ($this->is_file_exists($file)) {
require $this->helper_Folder . '/' . $file;
}
}
}
private function is_file_exists($path)
{
$full_path = $this->helper_Folder . '/' . $path;
if (!file_exists($full_path)) {
$this->flash->error("Helper File Missing: " . $full_path);
return false;
}
return true;
}
}
// to auto load file on every page through the bootstrap ("public/index.php")
$di->set('dispatcher', function () {
//Create/Get an EventManager
$eventsManager = new Phalcon\Events\Manager();
/*
* Load Custom function files which are in the helpers folder
*/
$loadHelper = new helperfile();
$loadHelper->include_file([
'calling_from_bootstrap_1',
'calling_from_bootstrap_2'
],false);
$eventsManager->attach('dispatch', $loadHelper);
$dispatcher = new Phalcon\Mvc\Dispatcher();
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
});
// To load it from controller
$loadHelper = new helperfile();
$loadHelper->include_file([
'calling_from_theController'
]);
That looks like it shoould work, but I think you underestimate the amount of work Phalcon can do for you.
An example of what is in the helper files would be useful. For the sake of this example I will assume that that it is like this:
app/
helpers/
ProductHelper.php
and in ProductHelper.php
class ProductHelper{
// code here
}
In your bootstrap where you have the loader you define your directories
$phalconLoader = new \Phalcon\Loader();
/**
* We're a registering a set of directories taken from the configuration file
*/
$phalconLoader->registerDirs(
array(
$phalconConfig->application->controllersDir,
$phalconConfig->application->modelsDir,
// path to helper dir here
)
)->register();
and then in your controller
public function productAction(){
$productHelper = new productHelper();
}
That should work. It is less code, so is simpler should run a bit faster (using phalcon's built in code rather than writing some php will always be faster)
If the code in the helpers is not in classes, or not named the same as the filename, then it probably should be. Makes things a lot simpler.
Di Enabled Version
class ProductHelper extends \Phalcon\DI\Injectable{
public $config;
public function myFunction(){
$this->config = $this->getDI ()->get ('config');
}
}
and in the controller
public function indexAction()
{
$helper = new ProductHelper();
$helper->setDI($this->getDI());
$helper->myFunction();
}
Alternatively when creating your DI
$di->set ('productHelper', function () use ($config, $di) {
$helper = new ProductHelper();
$helper->setDi ($di);
return $helper;
});
and in the controller
public function indexAction()
{
$helper = new ProductHelper();
$helper->myFunction();
}
I am using a Spotify library called MetaTune and was able to do this easily in CodeIgniter but with Yii there have been some teething issues however currently it has started saying:
Fatal error: Call to undefined method stdClass::searchTrack() in ....public_html/Yii/news/protected/controllers/NewsController.php on line 67
Howevever, the the function is there. The files in this library all have a .class.php suffix (e.g. MetaTune.class.php) and the libray files are all stored in:
yii/application/protected/vendors/Metatune
With Codeigniter I made an additional spotify.php outside of the folder and autoloaded that to my controller, but im not sure if this is necessary.
I have loaded it in my config.php with:
'import'=>array(
'application.models.*',
'application.components.*',
'application.vendors.metatune.*',
),
Here is the Controller code:
public function actionView($id)
{
$model=$this->loadModel($id);
$spotify = MetaTune::getSomething();
$hello = $model->title;
Yii::import('application.vendors.metatune.MetaTune');
$spotify->autoAddTracksToPlayButton = true; // Will add all searches for tracks into a list.
$spotify->playButtonHeight = 330; // For viewing the entire playlist
$spotify->playButtonTheme = "dark"; // Changing theme
$spotify->playButtonView = "coverart"; // Changing view
try
{
$tracks = $spotify->searchTrack($hello);
$tracks = $spotify->getPlayButtonAutoGenerated($hello);
}
catch (MetaTuneException $ex)
{
die("<pre>Error\n" . $ex . "</pre>");
}
$song = 'tracks';
$this->render('view',array(
'model'=>$this->loadModel($id),
));
}
Please also see the code below where it has a function called getInstance which doesnt work well with Yii for some reason and Im not sure if I can change this as I used this to import MetaTune into the CodeIgniter controller without any issues.
Just a part of the MetaTune.class.php code:
Yii::import('application.vendors.metatune.Artist');
Yii::import('application.vendors.metatune.Album');
Yii::import('application.vendors.metatune.Track');
Yii::import('application.vendors.metatune.CacheRequest');
Yii::import('application.vendors.metatune.MBSimpleXMLElement');
Yii::import('application.vendors.metatune.SpotifyItem');
Yii::import('application.vendors.metatune.MetaTuneException');
....
class MetaTune {
const CACHE_DIR = 'application/vendors/metatune/cache/'; // Cache directory (must be writable) relative to this file
const USE_CACHE = false; // Should caching be activated?
const CACHE_PREFIX = "METATUNE_CACHE_"; // prefix for cache-files.
const SERVICE_BASE_URL_SEARCH = "http://ws.spotify.com/search/1/";
const SERVICE_BASE_URL_LOOKUP = "http://ws.spotify.com/lookup/1/";
const PLAYBUTTON_BASE_URL = "https://embed.spotify.com/?uri=";
public $autoAddTracksToPlayButton = false;
private $list = array();
// Holds instance
private static $instance;
.....
public static function getSomething()
{
if (!isset(self::$instance))
{
$class = __CLASS__;
self::$instance = new $class;
}
return self::$instance;
}
.....
public function searchTrack($name, $page = 1)
{
$url = self::SERVICE_BASE_URL_SEARCH . "track?q=" . $this->translateString($name) .
$this->addPageSuffix($page);
$contents = $this->requestContent($url);
$xml = new MBSimpleXMLElement($contents);
$tracks = array();
foreach ($xml->track as $track)
{
$tracks[] = $this->extractTrackInfo($track);
}
if ($this->autoAddTracksToPlayButton) {
$this->appendTracksToTrackList($tracks);
}
return $tracks;
}
If you have any suggestions I would be most grateful. Thanks.
You didn't initialize $spotify anywhere, and php made it into stdClass by default, since you were assigning values like to member properties using that variable, but it failed when you tried calling unexisting method on it.
Solution: initialise it before you use it
$spotify = MetaTune::getInstance();
Well, is there something like before() method in kostache module? For example, if I have a couple of PHP lines inside of the view file, I'd like to execute them separately inside of the view class, without echoing anything in the template itself. How can I handle that?
You can put this type of code in the constructor of your View class. When the view is instantiated, the code will run.
Here is a (slightly modified) example from a working application. This example illustrates a ViewModel that lets you change which mustache file is being used as the site's main layout. In the constructor, it chooses a default layout, which you can override if needed.
Controller:
class Controller_Pages extends Controller
{
public function action_show()
{
$current_page = Model_Page::factory($this->request->param('name'));
if ($current_page == NULL) {
throw new HTTP_Exception_404('Page not found: :page',
array(':page' => $this->request->param('name')));
}
$view = new View_Page;
$view->page_content = $current_page->Content;
$view->title = $current_page->Title;
if (isset($current_page->Layout) && $current_page->Layout !== 'default') {
$view->setLayout($current_page->Layout);
}
$this->response->body($view->render());
}
}
ViewModel:
class View_Page
{
public $title;
public $page_content;
public static $default_layout = 'mytemplate';
private $_layout;
public function __construct()
{
$this->_layout = self::$default_layout;
}
public function setLayout($layout)
{
$this->_layout = $layout;
}
public function render($template = null)
{
if ($this->_layout != null)
{
$renderer = Kostache_Layout::factory($this->_layout);
$this->template_init();
}
else
{
$renderer = Kostache::factory();
}
return $renderer->render($this, $template);
}
}