In my MVC view files, there exist strings I may have a translation for. In a file with access to the database (the model), I can do:
$Lang->say('Welcome');
Here is what it's doing:
public function say($string) {
if (empty(self::$vocabulary)) {
self::$vocabulary = $this->loadLanguage($this->currentLanguageID()); // Load vocabulary for current language
}
if (isset(self::$vocabulary[$string])) {
return self::$vocabulary[$string];
}
return $string;
}
I need access to this say() function from within my view. Short of passing the entire vocabulary array to the view, how would I do this?
What you need to do is require_once your .php file that contains your class. You can then instantiate your class into a object like $Lang and call $Lang->Say() from your view.
For example:
require_once "file_that_holds_class.php";
$Lang = new classNameHere();
$result = $obj->Say("whatever_string_value");
echo $result;
Now you can do whatever it is you need to do with the string.
Related
Right now I have something like this in my mustache template:
{{#render}}{{widget}}{{/render}}
And I have a viewModel which contains this code:
public function render() {
$View = $this->_View;
return function($widgetName, Mustache_LambdaHelper $helper) use (&$View) {
$widget = $helper->render($widgetName);
return $helper->render($View->mustache->getPartialsLoader()->load("$widget.view"));
};
}
I think you can see what I am trying to do here. I am trying to render a partial who's name is contained inside the widget-key of the current context.
Now the thing is, I really don't like the format I'm using in the template. I would prefer if I could write something like this:
{{renderWidget}}
then I would need to be able to access the current context directly in some way.
public function renderWidget() {
return function($context) {
return $helper->render($View->mustache->getPartialsLoader()->load("$context[widget].view"));
}
}
Can anybody tell me if that is possible in one way or another?
My current implementation:
class SomeController extends AppController
{
function someaction()
{
$d['text'] = "ahoy!";
$this->render("someactionView", $d);
}
}
And in AppController:
function render($file, $data = "")
{
require "views/" . $file . ".php";
}
And the $data will be available in the views file. Is this a correct implementation? Are there any fallacies with this implementation?
And the $data will be available in the views file. Is this a correct
implementation? Are there any fallacies with this implementation?
Basically you do implement it like the most frameworks do. There's a couple of problems with that:
A controller takes an input and sends an output (which breaks the Single-Responsibility Principle)
A view is tightly coupled to HTML. Because of this, you cannot re-use the same view for another stuff, like XML, JSON.
If you do require "views/" . $file . ".php"; in render() method - you again tighly couple it. What if you change the location of views? Then you would have to slightly rewrite your method. This approach merely kills reuse-ability.
To refresh your basic knowledge:
Controller (also known as Editor)
Serves only singular purpose. It changes model state - that is, it should take an input that comes from $_POST, $_GET, $_FILES, $_COOKIE. In controller only variable assignment should be done and nothing more.
class Controller
{
public function indexAction()
{
$this->view->setVar('age', $this->request->getPostParam('age'));
$this->view->setVar('user', $this->request->getPostParam('user'));
//...
}
}
View
A view has a direct access to a model. In order to make make views more re-usable and maintainable you'd better pass required things as function parameters (or via setters)
class View
{
public function render($templateFile, array $vars = array())
{
ob_start();
extract($vars);
require($templateFile);
return ob_get_clean();
}
}
How the view should be initialized and how the variables should be passed to it?
First of all - a view should be instantiated outside MVC-triad. Since a controller writes either to view or model - you'd pass variables to view via controller.
$model = new Model();
$view = new View($model);
$controller = new Controller($view);
// This will assign variables to view
$controller->indexAction();
echo $view->render();
Note : In real world scenario, a model isn't a class, but abstraction layer. I call it Model for demonstration purposes.
IMO the render() method belongs to the view and not to the controller. The code should look like this:
Controller:
class SomeController extends AppController
{
function someaction()
{
$d['text'] = "ahoy!";
$view = new SomeActionView();
$view->assign('data', $d);
echo $view->render();
}
}
View Base Class:
class View
{
protected $data;
function render($template) {
ob_start();
// you can access $this->data in template
require "views/" . $template . ".php";
$str = ob_get_contents();
ob_end_clean();
return $str;
}
function assign($key, $val) {
$this->data[$key] = $val;
}
}
Extend View class
class SomeActionView extends View
{
public function render($template = 'someActionTemplate') {
return parent::render($template);
}
}
Is this a correct implementation? Are there any fallacies with this implementation?
Short answer: no and several.
First of all, what you have there is no a view. It's just a dumb php template. Views in MVC are instance, that contain the UI logic for the application. They pull information from model layer and, based on information they receive, create a response. This response can be simple text, JSON document, a HTML page assembled from multiple templates or simply a HTTP header.
As for controller, it's only task is to alter the state of model layer and (on rare occasions) the current view. Controllers do not initialize the views nor do the populate templates.
I have two functions loadView() and render() in my view class.
public function loadView($view){
if(file_exists(APP.'view/'.$view)){
require(APP.'view/'.$view);
}
else{
die(APP.'view/'.$view.' not found.');
}
}
public function render($view,$data = array()){
if(!empty($data)){
extract($data);
ob_start();
//$this->loadView($view); -------------- not woriking
require(APP.'view/'.$view); ------ working
$this->output = ob_get_clean();
echo $this->output;
}
}
Whenever I call the loadview function from the render its not working. But if I include the view file directly after extracting data, it works. Anybody can tell me Why is this happening here or any solution ?
This will not work, because require() includes the required code in the place of the require() statement and in the scope of that location. In this case, the scope is the loadView() method. Within that method scope the extracted variables from the render() method are not available.
Instead of finding a solution for that, I would encourage you to use a template engine for your views like Twig. This is standard practice in MVC frameworks. Then, you could do something like this:
public function render($view,$data = array()){
$template = $twig->loadTemplate($view);
echo $template->render($data);
}
What is the best way to make a variable accessable to all classes.
For example, I want to have a configuration file (Call it config.php) that is going to have a variable like so:
$server_url = "www.myaddress.com";
And I have a main library type file that contains a bunch of classes that need to access the $server_url. So here begins that main library file:
require 'config.php';
class one {
function a() {
$html = "<a href='$server_url/main.php'>LINK</a>"
return $html;
}
function b() {
$html = "<a href='$server_url/about.php'>LINK</a>"
return $html;
}
}
class two {
function new() {
$html = "<a href='$server_url/blah.php'>LINK</a>
}
}
What would be the best way to make $server_url from the config.php available to every function? Or at least available to all the functions in a class?
Personally I would use a static entity to hold all configuration values.
Usually, most php applications have a single entry point (index.php) that can load up the config.php file and make the static entity available from that point.
If your application has multiple entry points, then you will need to include config.php in each of these points.
Something like this:
<?php
class Configurator{
private static $_configuration = array();
public static function write($key, $value) {
self::$_configuration[$key] = $value;
}
public static function read($key) {
return self::$_configuration[$key];
}
}
Configurator::write('server', 'http://localhost');
Configurator::read('server');
?>
CakePHP has a similar class: http://api.cakephp.org/view_source/configure/
Make the config into a class in itself and use a static methods either along the line of serverUrl() or get('server_url'). Then call them like any other static methods to classes (I'll choose the latter in this example):
$html = "<a href='" . Config::get ('server_url') . "/main.php'>LINK</a>";
The config class could be pretty slim, use a constructor like:
public function __construct (array $config)
{
foreach ($config as $key => $value)
{
$this->$key = $value;
}
}
And add the get() method along these lines:
public function get ($key)
{
return $this->$key;
}
This way you can read the config from an array that you can have as a separate, actual config file, and reuse the same code for multiple projects.
You'll also be able to access the variables from anywhere in the project and you'll get a sort of pseudo-namespacing (in case the project needs to run on an older version of PHP).
Please, don't copy the code verbatim, it's written as an example.
think of globals are evil. Try to use design patterns to get access to some configs globally.
I'm a big fan of singletons to get global access to objects, arrays or other data-types.
<?php
class st {
static $_this;
function __construct(){
self::$_this = $this;
}
static function &getInstance(){
return self::$_this
}
static function set($key, $value){
self::$_this[$key] = $value;
}
static function &get($key){
return self::$_this[$key];
}
}
// Usage
new st();
st::set('foo', 'bar');
// In some class
st::get('foo'); //return 'bar'
// Or when there are some classes/objects
st::getInstance()->foo->bar();
$st =& st::getInstance();
$st->foo->bar();
?>
Roughly wrote down a small singleton, but don't know whether there is a syntax error.
While handling with getInstance it's certain that you define the variable by reference =&
Define a constant in config.php like:
define('SERVER_URL', '...');
In your class:
echo SERVER_URL;
What works for me the best is to use a config file like config.ini
And then to use $my_config = parse_ini_file(file path/config.ini');
Now everywhere in my code including inside functions and classes, I will use
The PHP superglobal like this:
$GLOBALS["my_config"]['my_global_var']
I am working on creating my own very simple MVC and I am brainstorming ways to go from the controller to the view. Which involves sending variables from a class to just a plain old PHP page.
I am sure that this has been covered before, but I wanted to see what kind of ideas people could come up with.
//this file would be /controller/my_controller.php
class My_Controller{
function someFunction(){
$var = 'Hello World';
//how do we get var to our view file in the document root?
//cool_view.php
}
}
Some kind of hashtable is a good way to do that. Return your variables as association array which will fill all the gaps in your view.
Store your variables as a property in your controller object, then extract them when rendering
class My_Controller {
protected $locals = array();
function index() {
$this->locals['var'] = 'Hello World';
}
protected function render() {
ob_start();
extract($this->locals);
include 'YOUR_VIEW_FILE.php';
return ob_get_clean();
}
}
You can define those magic __get and __set methods to make it prettier
$this->var = 'test';
I'm also developing my own simple MVC and the most simple way to do it is ...
class My_Controller
{
function someFunction() {
$view_vars['name'] = 'John';
$view = new View('template_filename.php', $view_vars);
}
}
View class
class View
{
public function __construct($template, $vars) {
include($template);
}
}
template_filename.php
Hello, <?php echo $vars['name'];?>
I highly recommend you to take a look at PHP Savant http://phpsavant.com/docs/
I'd checkout Zend_View and how it accomplished view rendering.
You can get the source of View and AbstractView on github - unfortunaly I don't find the current repository (in svn) that easy to browse.
Essentially the view variables are contained in a View object (which your controller would have access to), then the template (plain old php document) is rendered inside that object. That method allows the template access to $this.
It would be something like:
<?php
class View
{
public function render()
{
ob_start();
include($this->_viewTemplate); //the included file can now access $this
return ob_get_clean();
}
}
?>
So in your controller:
<?php
class Controller
{
public function someAction()
{
$this->view->something = 'somevalue';
}
}
?>
And your template:
<p><?php echo $this->something;?></p>
In my opinion this pattern allows you much flexibility with the view.
I created my own MVC for the free PHP course I'm conducting for a handful of people wanting to get better at PHP.
By far the best way to do this is to use the Command + Factory pattern.
E.g.
interface ControllerCommand
{
public function execute($action);
}
In each controller:
class UserController implements ControllerCommand
{
public function execute($action)
{
if ($action == 'login')
{
$data['view_file'] = 'views/home.tpl.php';
}
else if ($action == 'edit_profile')
{
$data['view_file'] = 'views/profile.tpl.php';
$data['registration_status'] = $this->editProfile();
}
return $data;
}
}
From your main front controller:
$data = ControllerCommandFactory::execute($action);
if (!is_null($data)) { extract($data); }
/* We know the view_file is safe, since we explicitly set it above. */
require $view_file;
The point is that every Controllercommand class has an execute function and that returns its view and any data for that view.
For the complete MVC, you can access the open source app by emailing me at theodore[at]phpexperts.pro.