I am working on a fairly large CodeIgniter project. There will be more pages than I can count. Right now, I have a few different headers, depending on what page the user is visiting and also depending on if the user is logged in. For example, if the user is not logged in (no session variable stored) then I want to display header1 which shows some basic stuff like "Signup, Login". If the user IS already logged in, I want to show different menu items like "logout". That's a very basic example. The changes can be pretty extensive, so it wouldn't make sense for me to have one header file with control flow logic in it.
I'm looking for a way to include the appropriate header without having to write $this->load->view('header') in every method I have in my controllers. It looks like another option is to write that line in every view that I have. To make this dynamic I created a MY_Controller file and included something like this in it:
public function get_header()
{
if ($this->session->userdata('user_id')) {
$this->load->view('headers/logged-header');
} else {
$this->load->view('headers/header-home');
}
}
Then I include <?php MY_CONTROLLER::get_header(); ?> in places that I need it (i.e inside methods or views.)
Is there a way to "hook" this in somehow, where I can simply write this code once, and then the application process it every time it loads one of my methods that call up a page?
This is a very typical need that could be easily handled by using the OOP, which CI is using in all of its controllers.
Since all of your self-defined controllers must inherit from CI_Controller, you can just write a base controller(class) that inherits from CI_Controller, which do some very basic things like :
do some basic operation in its __construct that all of its subclass may need(e.g. auto login logic, and you can put get_header here)
provide methods that all of its subclass may need like right check, pagination widget generator...
Probably the easiest way would be to call that function in the constructor method of all your controllers. That way its called every time for every method in the class. Just make sure you call the parent constructor first.
Related
i know that this question already has been asked. But i still dont understand it.
Right now im trying to understand how to make use of the MVC Modell.
But i dont get how to integrate everything and right now I am hesitating to use a framework before i really get it.
What I try to achieve:
I want to have a header, a footer and a menu which remain the same all the time.
Like this:
HEADER
MENU
{CONTENT}
FOOTER
So my thinking is:
My Controller gets some Information, lets say its a User-ID.
Controller calls a method from the Model:
Get all DATA from USER with ID: 1
Then the controller passes the DATA into a view, lets say a list.
So where to go from here?
Should the controller pass this view into another view?
Like:
$site = new View(); <br>
$site->wholeSite($content);
and then site is something like:
HEADER
MENU
{$content}
FOOTER
Please excuse my simple approach, im just trying to get the basic idea behind it and i already read the first 20 pages of googling it.
Just cant get my head around it.....
It would be really nice if you would explain it for an Beginner :)
Thanks in advance!
you can call multiple views from the control like:
$this->load->view('header',$data);
$this->load->view('menu',$data);
$this->load->view('content',$data);
$this->load->view('footer',$data);
If you have a nested div then you can use regular include function like from a particular view file.
include('footer.php');
According to the path of the view file. The data also gets transferred from parent view to the included file.
Generally MVC apps have a universal layout view which contains the header, footer and menus. Sometimes data required for that is handled outside the controller, in say your init script or bootstrap.
An MVC purist might pass all data the layout requires from the controller, but that can be repetitive, although setting the data in an abstract controller construct might alleviate that.
On the question of views and sub-views there are generally two solutions:
A.) You have one principle view which all required data is passed to. In that view sub-views (partials) are called, and data is passed down into them.
B.) You instantiate all your views in the controller, passing all the data they need directly into the view, you would then pass views as parameters into other views.
Both methods have their pros and cons. I find the first option leads to less Fat Controllers, so I usually go with that.
I'd recommend you learn an existing MVC framework ( CodeIgniter, now defunct is basic enough to make it a good entry point), as that will show you how things are done.
there are cases when i require to run the same logic both in controller and view, i thought they may have a shared code, for example when to display an edit link and the same logic should check for edit permission in controller , where should i keep such code so that same can be used in controllers as well as views, I am okay to write a a component wrapper and a view wrapper for this method but the core logic should be common.
Some mentioned bootrap is a place but putting there do i have all the cake defined parameters or constants available from that location? or there is a better place
EDIT
I gave only authentication related example but there can be more cases like, a view helper for displaying data/time based on offset time set in database (system time + offsetime), i have been forced to use of same code in controllers also (ajax output). What does it imply that in common code we not only have shared logic but also some shared data too, so for only session classes appears to be providing shared data!
On the whole, I'm inclined to say you're not going about this the right way. If you have to include the same piece of logic in both the controller and the view, chances are you're doing the same work twice, which is always a bad idea. DRY (Don't Repeat Yourself) is something you probably heard about a million times before.
However, in the case you mention (authentication): this is done in the Controller, which relies on the Model layer to find out if the user has the permissions to see/use edit links.
Based on the data the controller receives from the Model layer, it should do one of the following things:
choose to render a specific view (with/without edit links)
Present the user with an error view
redirect to the login
For the first case, an editable and non-editable view, you can choose to use the same view script, and use a helper to pass on the session information to the view. Effectively giving the view the means to check a users' session, check if the user in question has the rights required to see the edit links and render them...
Another simple fix would be to set a property of the view to true for editable and false for non-editable in the controller, and check that bool flag in the view. No extra logic required.
The main thing here is that what you're after is authenticating the user. By the time you reached the view, the route is a given, there's no way back. If you find yourself still having to validate the users' identity, you've made a mistake at an earlier point: the controller, and model layer is where this kind of core logic should reside.
A view contains no logic other than its own: a loop or 2, some if-else's... nothing more.
The rule of thumb, then is: the first thing the controller does is authenticate the user. The authentication itself is the concern of the Model layer: no actual core/business logic should be in the controller. Based on the findings the model layer returns (authentication failed, or user has rights to do X, but not Y), the controller can redirect, throw errors or choose to render a specific view.
Only after all these things are performed the view is brought in. If there's a user in play, the view can assume that this user is valid, and has the rights required to see its contents. The view does not validate data, nor does it authenticate users.
To authenticate a user the Cake-specific way there's a core Authentication component that seems to be quite well documented. See if you can't use that...
You can add simple functions to bootstrap, but another option would be to create a class in app/Lib/ using static methods. Then from any controller you can include the library and use the methods defined:-
App::uses('MyLibrary', 'Lib');
If all controllers need to use these just include the library in AppController.
For your views I'd then consider defining a View Helper that would apply the methods used in MyLibrary.
I'm returning a list of people from MySQL
So I have a model, a controller and a view.
As I'm outputting the list of people on a page, I need to check the length of the person's name and dynamically add a class to the that it's displayed in.
Before this was CI, I created a simple function called 'name_class' and put that in a common.php include. On the page, as I was outputting people's names, I'd pass the name to that and it would echo a class on the if the name was over a certain length.
In code igniter, where is the best place to put this function? I thought perhaps as a private function on the controller but then, can I access that from the view?
It seems more logical to me that this belongs as a function within your view. If what your doing has something to do with how you are displaying the data, then it should be in the view layer of your code.
Though CodeIgniter is geared towards web applications, you should always consider the fact that the MVC design pattern is not specific to web pages/sites. The view layer of MVC is used to change how the data provided by the model is displayed or provided to the UI or client. You should attempt to put any client or UI based information or logic into the view and keep it hidden from the Model and Controller layers.
For example, it looks like your view is outputting the HTML directly which is consumed by the client side browser to display. Here is makes a lot of sense to output HTML, and provide the CSS classes needed based on the data provided by the Model. Now, consider what would need to change within your application if you decided to use a client library that accepts your data in JSON format and displays it. If your application's architecture is correct, you should only need to change the View to provide the information in JSON format and you shouldn't be leaving view specific code (i.e. your class logic) in another layer.
Not as a private function on the controller, but as a public function on the controller. If you created a function like this:
public function _test(){
echo "HERE";
}
then you could access it in the view with get_instance()->_test();. Make sure you begin the function name with an underscore to make sure it is not executable from the browser.
EDIT: you could also use a helper as documented at http://codeigniter.com/user_guide/general/helpers.html.
It definitely goes in the view, or if you can make the function useful enough to be used for more than just this one instance, put it in a helper - but still only call it in the view.
The view is your output. HTML belongs in the view whenever possible. class is HTML, so naturally you want to restrict it to your view.
Think of it like this (though this is a broad generalization):
Controller = Input
Model = Processing
View = Output
There is also no good way to access Controller functions directly from the view, you'd have to run it in your controller and pass it as data to the view.
I created a page using the MVC structure called 'sections' ( view is located in the app/views/sections folder, model in the model folder and the controller in the controller folder) when i request the variable $test, it works fine without any errors..
When i want to request this variable in my home.ctp, it provides me with an error, saying that the variable is undefined..
Is there any way in cakePHP to request this variable on any page you want it to?
Thnx in advance!
In the MVC stack, you need to set variables with data in your controller, and then pass them out to your view.
So in your example, you'll want to $this->set('myvar',$item); in your SectionsController, then in your view, you will be able to echo $myvar.
Be sure to set this in the home() method of your Sections controller, otherwise it won't be available in your home view.
Standing on the shoulders of deceze's comment and DavidYell's answer, I think they've managed to scratch out a decent view of what you're trying to get to. Maybe. So with that loose understanding of what you're seeing and what you have...
By default, the PagesController::display() method generates the homepage view (home.ctp). I suspect that this is what you're talking about. That said, the variable you're setting in a method of your SectionsController won't be available to your homepage which is created by a different method in a different controller. If you want a variable available to all views there are several things you can do:
You can set the variable in your config/core.php file (not generally recommended)
You can set it in config/bootstrap.php if it's a constant. By that, I mean that it's a value you're going to hard code, not something dynamically generated. Whether you create the variable as a constant doesn't matter.
You can set in in your AppController in a beforeFilter() or beforeRender() method. All of your custom controllers (assuming you've followed protocol) inherit from the AppController. If you choose this path, make a copy of cake/libs/controller/app_controller.php and place it in your app/ directory.
Those are the ways that I think will best meet your needs as I understand them.
Rather than use a full-blown PHP MVC, I'm designing one that will best-fit my uses. I have the basic framework done, and have coded the models and controllers I'll need to run my website.
Now I'm moving onto the Views, and I've encountered a small dilemma. My approach is working fine for me, but for future reference, I want to know if what I'm doing is a bad habit to get into.
What I'm trying to do:
In my View, I'm calling a Model that runs my authentication system, and requesting the login status of a user. I then use that boolean to decide whether to show certain elements within the view, and where to place others.
Should I be designing separate views for each login status, or is this approach fine? However, if I'm going to be implementing this MVC into the work I'm doing for my clients, I need to use the best practices.
Any advice would be appreciated!
Can I call the model from the View?
Yes, you can. As long as you maintain the separation of concerns between M,V and C, you are free to call upon the Model (or the Controller) from the View. Most MVC diagrams show a bidirectional connection at least between View and Model. What you don't want to do though, is place logic/code from the Model (or the controller) into the View and you don't want to modify the model from there.
For example, you might have a widget on your page that aggregates the latest ten blog posts headlines from your favorite blogs on each page of your website. You get the headlines by calling, say MyFavFeeds::getLatest(); in your model. What are your options now?
You could add the code to fetch the headlines into the controller, but that would require you to replicate it in each and every controller action, which is against the DRY principle. Also, the controller's concern is handling user input for specific actions and fetching the headlines on each call is likely not even related to these actions.
If your architecture supports it, you could fetch that data in some sort of preDispatch hook, that is, the headlines get loaded and injected into the View from a plugin or callback. That would be DRY, but a second developer might not be aware of that plugin and accidently overwrite the variable holding the headlines from his controller action. And there might be cases in which you wouldn't want to load the headlines, e.g. when just rendering confirmation pages for form submissions, so you'd have to have mechanism for disabling the plugin then. That's a lot to consider.
You place the call to (not the code of) MyFavFeeds::getLatest() into the View or Layout template or, better, a ViewHelper, that encapsulates the call to your model class and renders the widget. This way you don't have to worry about overwriting any variables or repetition. And when you don't need the headlines on your view, you simply don't include it.
About your other question:
In my View, I'm calling a Model that
runs my authentication system, and
requesting the login status of a user.
I then use that boolean to decide
whether to show certain elements
within the view, and where to place
others.
Authentication is something you will want to do early in the application flow, before any controller actions are called. Thus, you should not run your (entire) authentication system in the View. The actual authentication is not View-related logic. Just requesting the user status after authentication, on the other hand, is okay. For instance, if you want to render a widget showing the user name and giving a login/logout button, it would be fine to do something like
<?php //UserHelper
class UserMenuHelper
{
public function getUserMenu()
{
$link = 'Logout';
if(MyAuth::userHasIdentity()) {
$link = sprintf('Logout %s',
MyAuth::getUsername());
}
return $link;
}
}
If you got larger portions of your GUI to be modified by a User's role, you might want to break your View apart into partial blocks and include them based on the status, instead of writing all the HTML into a View Helper.
If you are only looking to render a navigation based on the user role, have a look at Zend Framework's Zend_Navigation and Zend_Acl to see how they do it.
You can, but you shouldn't. Except for a few extreme cases (and branching your view based on logged-in status is definitely not an "extreme case"), it's pretty much always A Bad Idea to call model stuff from a view.
What you probably want to do in your situation is pass the boolean to the view through the controller. That way, if you change something about the User model, the view doesn't have to know, so long as the controller keeps the behavior the same.
While I only know enough about MVC to get myself in trouble, I've always lived by the fact that unless your view is STRICTLY user interface, then it shouldn't be there.
Also, I've also ran with the idea of thin controllers, fat models.
Going off of these, I'd suggest adding a method to your authentication system model that returns the appropriate view to render and passes that to the view.
Okay, I would really try to keep my Views as logic-free as possible. If you need to switch anything based on e.g. the result of a model method, put a controller in place that delegates the rendering.
Just make sure you follow the basic idea: Do your stuff in the models, Tell your models what to do from your controllers and also tell your views what to show from your controllers.