MVC confusion, views are not templates - model-view-controller

I have been reading an article for many times and yet, I can't still understand some parts.
Link for the article : Model-View-Confusion part 1: Why the model is accessed by the view in MVC
The code below is the one I think I am confused on.
class ListView extends View {
public $model;
public $template;
public $listTemplate;
public $errorTemplate;
public $itemName = 'items';
public function output() {
$result = $this->model->findAll();
if (count($result) > 0) {
$this->template = $this->getTemplate($this->listTemplate);
$this->template->addSet($this->itemName, $result);
} else {
$this->template = $this->getTemplate($this->errorTemplate);
}
return $this->template->render();
}
}
And the controller looks like this :
class UserController extends Controller {
public $viewName = 'ListView';
public function showList() {
$this->view->model = $this->model->user;
$this->view->listTemplate = 'UserList.tpl';
$this->view->errorTemplate = 'ErrorNoUsers.tpl';
}
}
As I can understand the template was assigned to a result of a method inherited from the View named getTemplate passed with a method from the View again named listTemplate
like this $this->getTemplate($this->listTemplate)
What I am confused on is that the $template suddenly had a method, which means it becomes a class . right here $this->template->addSet($this->itemName, $result); and `$this->template->render();
Do you have any idea what happened right there?

Firstly, full disclosure: I'm the author of that article.
It wasn't intended to be a complete code solution but an example of the benefits of applying a full MVC separation of concerns instead of the PAC pattern which is claimed to be MVC by most of the PHP community. How it works behind the scenes is beyond the scope of the article. However, what it was supposed to be demonstrating is that the View should encapsulate the template. In that example the template object could be a Smarty Template, a Twig instance or anything.
In hindsight I shouldn't have named the variables which reference file names "template". The confusion you have is understandable: $this->listTemplate; is a reference to the file containing the template code, and $this->template; is an instance of a template object (could be smarty, twig, anything else) that loads the file referenced in listTemplate.
I'll have a look at amending it to be clearer. I wrote that two years ago and there's several things I'd word differently and explain slightly better in the examples if I wrote it again.

Related

Laravel: Grab data from the Controller from inside a view composer

Atm I'm creating this view composer for fun. It is collecting .js filenames and then passing it to the layout to be linked. The filenames used depend on the current page. For example a lower ranked page like Slides, doesn't include ajax requests used in UserManagement. Please don't ask me why I would do this xD. Im planning to validate requests anyway. Just being bored.
Anyways, as I'm quite new to laravel I'm still looking for more efficient ways to do things.
Atm Im accessing the file names staticly. The Controller now looks like this
class Controller extends BaseController
{
public static $js_file_names = [];
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
In the pagecontroller I construct the filenames:
class SlidesController extends Controller
{
public function __construct()
{
parent::$js_file_names = ['ccs', 'ajax-updates'];
}
And finaly I retreive them inside the registered Viewcomposer like this:
(during development $use_filenames has all files as default)
public function __construct()
{
$filenames = Controller::$js_file_names;
if( !empty($filenames) )
$this->use_filenames = $filenames;
var_dump($this->use_filenames);die;
}
It all seems to be working fine, but the big question is, is there a better way to access controller data from inside a viewcomposer? Every time I try to google this, I get results like 'passing data to views' etc, which is not rly the problem.
Update:
Another idea I had is to store all the filenames to be used in an array inside the viewcomposer itself, and check if the current page exists in that array. This would keep the controllers cleaner.
Using a view composer doesn't really make sense in this situation. Since your controllers already 'know' which files they intent to share, you may as well just pass them to the view like so:
class SlidesController extends Controller
{
public function __construct()
{
View::share('user_filenames', ['ccs', 'ajax-updates']);
}
}
A composer is more for sharing concrete elements such as collections of users, a service provider or some other class instance, for example.

Codeigniter models loaded in controller overwritten by models loaded in models

I'm having Codeigniter object scope confusion.
Say I load a model in a controller:
$this->load->model('A');
$this->A->loadUser(123); // loads user with ID 123
// output of $this->A now shows user 123
$this->load->model('B');
$this->B->examineUser ();
// output of $this->A now shows user 345
class B extends Model
{
public function examineUser ()
{
$this->load->model('A');
$this->A->loadUser(345); // loads user with ID 345
}
}
I would have thought that $this->A would be different from $this->B->A but they are not. What is the best solution to this issue? It appears the ->load->model('A') in the examineUser () method does nothing because it was loaded in the controller. Then the call to loadUser () inside that method overwrites the stored properties of $this->A. This seems like a bugfest waiting to happen. If I needed global models, I would have use static classes. What I wanted was something scoped pretty much locally to the model object I was in.
Is there a way I can accomplish this but not go way outside of CI's normal way of operating?
Followup/related:
Where do most people put there "->load->model" calls? All at the beginning of a controller action? I figured it would be easier -- though perhaps not excellent programming from a dependency injection perspective -- to load them in the model itself (construct or each method).
Whenever you use the Loader Class ($this->load->), it will load the object into the main CI object. The CI object is the one you keep referring to as $this->. What you've done is load model A twice into the CI object.
Essentially, all object loaded using the Loader class goes into a single global scope. If you need two of the same type, give them different names, as per $this->load->model('A','C'). I don't know of any way around it unless you revert to using bog-standard PHP.
In my team's code, we generally load the models in the controller's constructor, then load the data to send to the view in the function, often _remap().
This is not how the loader works sadly. CodeIgniter implements a singleton pattern, which will check to see if the class is included, instantiated and set to $this->A then will be ignored if loaded again. Even if you are inside a model, $this->A will be referenced to the super-instance via the __get() in class Model. Alis it, or just do:
class B extends Model
{
public function examineUser ()
{
$user = new A;
$user->loadUser(345); // loads user with ID 345
}
}
Here's what I've decided to do, please comment if you have advice:
I've extended the CI Loader class:
<?php
class SSR_Loader extends CI_Loader
{
function __construct()
{
parent::__construct ();
}
/**
* Model Retriever
*
* Written by handerson#executiveboard.com to create and return a model instead of putting it into global $this
*
* Based on original 2.0.2 CI_Loader::model ()
*
*/
function get_model($model)
{
if (empty ($model))
{
return;
}
$name = basename ($model);
if (!in_array($name, $this->_ci_models, TRUE))
{
$this->model ($model);
}
$name = ucfirst($name);
return new $name ();
}
}
Do any CI guru's see a problem with that before I invest time in changing my code a bit to accept the return obj, ala:
// in a controller:
public function test ($user_id=null)
{
$this->_logged_in_user = $this->load->get_model ('/db/users');
$this->_viewed_user = $this->load->get_model ('/db/users');
$this->_logged_in_user->load($this->session->userdata ('user.id'));
$this->_viewed_user->load($user_id);
}
I could also do private $_logged_in_user to make it available in the controller but positively force it to be limited to just the current controller and not spill anywhere else, or I could just do $_logged_in_user = $this->load->get_model ('/db/users'); and limit it to just the current method, which is probably what I'll do more often.
This seems like a pretty straightforward way to "fix" this issue (I say "fix" b/c it's not really a bug, just a way of doing things that I think is a bad idea). Anyone see any flaws?

Showing a view in Zend Framework

I've never worked with Zend Framework before, but I've worked with others (CodeIgniter, Kohana, etc). Right now I was asked to just show a view that wasn't existing so I started looking into the documentation and examples I could find and I've always found examples that use the Model part of the MVC, but in this case I just need to load a view and I can't figure out how to do that. I have this:
File: "BookController":
require_once("Initiate.php");
class BookController extends Initiate {
public function init() {
parent::init();
}
public function bookAction(){
#$client = Zend_Auth::getInstance()->getIdentity();
$view = new Zend_View();
echo $this->view->render('book.phtml');
#$this->view->assign("book", $client);
#echo $this->view->render('book.phtml');
}
the view is called "book.phtml" and is found in /application/views/scripts/bookapi/
What am I missing?
Given that eveything is setup correctly with MVC, your Controller should extend Zend_Controller_Action:
class BookController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->funnyText = 'This is a funny text.';
}
}
Then, in your application/views/scripts/book/ folder, there has to be a index.phtml. Which could look like that:
<p>
<?php echo $this->funnyText; ?>
</p>
That's it, nothing more required.
Btw. doesn't make sense to have a controller called book and then also an action called book
you don't have to manage view yourself there is an Action Controller Helper called View Renderer it does your job for you all you need to follow is its naming convention i.e if your controller name is 'BookController' then its view file should be located at views/scripts/book/boo.phtml .
Assuming BookController extends Zend_Controller, it should automatically setup the view and you shouldn't have to render it. Your view file should be in /application/views/scripts/book/book.phtml. Follow the quick start for more information.

How To Start Using Kostache?

I just asked a question ( Templates In Kohana 3.1 ) about templates and now I know that I should use Kostache. It's a module for the Mustache template language.
Anyway, I just enabled Kostache module for my Kohana 3.1 and all works. It's installed correctly! What to do next? How to use it?
Where should I put my views now?
What my controller should extend?
How to assign variable?
How to make header, footer etc. for views?
Maybe there are step to step guide for it? This and this won't help me a lot...
Where should I put my views now?
View classes contain logic for your templates and by convention should be stored in classes/view/{template name}.php
Templates contain your HTML and should be stored in the templates directory in the root of your module, e.g. templates/login.mustache
By default kostache will try and work out the location of the template based on your view class' name.
If your view class is called View_Admin_Login then kostache will look for templates/admin/login.mustache
What my controller should extend?
You do not need to extend any special controllers, the normal Controller will work fine as a base.
How to assign variable
Controller:
$view = new View_Admin_Login;
$view->message = 'Hello';
$this->response->body($view->render());
Template:
{{message}}
Of course, any methods or variables you declare in your view class will also be available in
the template. If there is a class variable and method with the same name then the method will always take precedence over the variable.
How to make header, footer etc. for views
It will help if you read the kostache guide. The idea is that your views extend Kostache_Layout, see also the layout template
There's lots of demos and examples in both of the repositories that you said won't help you.
Try this...
//application/classes/controller:
class Controller_Test extends Controller {
public function action_index()
{
$view = new View_Home;
$this->response->body($view->render());
}
}
//application/classes/view/Home.php:
class View_Home {
public $name = "Chris";
public $value = 10000;
public function taxed_value() {
return $this->value - ($this->value * 0.4);
}
public $in_ca = true;
protected $_layout = 'home';
}
//application/templates/home.mustache:
Hello {{name}}
You have just won ${{value}}!
{{#in_ca}}
Well, ${{ taxed_value }}, after taxes.
{{/in_ca}}
In your APPPATH/classes/controller/Test.php:
class Controller_Test extends Controller{
public function action_index()
{
$renderer = Kostache::factory();
$this->response->body($renderer->render(new View_Test));
}
}
In your MODPATH/KOstache/classes/view/Test.php:
class View_Test
{
public $name = "Chris";
public $value = 10000;
public function taxed_value() {
return $this->value - ($this->value * 0.4);
}
public $in_ca = true;
}
In your MODPATH/KOstache/classes/templates/test.mustache:
Hello {{name}}
You have just won ${{value}}!
{{#in_ca}}
Well, ${{ taxed_value }}, after taxes.
{{/in_ca}}
In the following example, do not pay attention to naming classes and inheritance:
More examples on GitHub

Codeigniter common templates

Let's say that I have a website that has 100 different pages. Each page uses a common header and footer. Inside the header is some dynamic content that comes from a database.
I'd like to avoid having to have code in every single controller and action that passes this common code into the view.
function index()
{
// It sucks to have to include this on every controller action.
data['title'] = "This is the index page";
data['currentUserName'] = "John Smith";
$this->load->view("main_view", data);
}
function comments()
{
// It sucks to have to include this on every controller action.
data['title'] = "Comment list";
data['currentUserName'] = "John Smith";
$this->load->view("comment_view", data);
}
I realize that I could refactor the code so that the common parts are in a single function and the function is called by the action. Doing so would reduce SOME of the pain, but it still doesn't feel right since I'd still have to make a call to that function every time.
What's the correct way to handle this?
One way I have been doing this is to extend the default controller class. You can read up on extending classes with MY_Controller in the user guide. Inside this extended class you can include something that you ALWAYS want to do, like render the page header template before the main content, or authorise a users access etc.
class MY_Controller extends Controller {
function __construct()
{
parent::Controller();
//code to always do goes here
echo 'Always print this comment';
$this->load->view('partials/template_start');
}
}
Then you can have your normal controller class extend THIS class by using
class MyControllerNameHere extends MY_Controller {
function __construct()
{
//setup here
}
function index()
{
echo 'Only print this bit when this method is called';
$this->load->view('partials/MYPAGENAMEHERE');
}
}
There are other ways of doing this, I use a mixture of the above and William's Concepts Codeigniter Template library. Do a bit of searching - there are a few solutions for you.
I had a similar situation. I created an 'includes' folder, and in there put a file that had the repetitive code from my controllers. Then in the controllers just include('/path/to/includeFile.php');
Don't know if it's the "correct" way, but it works well for me.
I ran across this after a search of their site. http://codeigniter.com/wiki/Header_and_footer_and_menu_on_every_page/ I'll review this page and its links, then post my thoughts.

Resources