So let's say i have a controller named pages.
with this function.
function __construct(){
parent::__construct();
$this->auth['result'] = $this->is_logged_in();
}
The above code, calls for the extended function 'MY_Controller' which has a function called is_logged_in() and will simply fetch session data.
My first question would be, am i doing this authentication correct? or am i simply under-using it?
Anyways, my main question is regarding loading views.
Say i have many views, some views are accessed only by TYPE A user that has an access level different to that of TYPE B user.
Say both accounts are created differently, like in some online websites where there are users and shopowners that registered to advertise their shop irl, or for example, online travel. One account is user(normal/travelers) and one account is hotelowner(owns a hotel irl). So both will have overlapping views and some dedicated views for them.
How do i go about it when loading views?
Say in my controller i got functions,
public function view_home(){
}
public function view_userprofile(){
}
public function vieW_hotelprofile(){
}
You get my idea from above, i have separate functions for loading different views. But my question is that, how do i go about making it into one big function?
like for example,
public function view($page ='home' ){
}
the view function would be like a terminal for different accounts to access the views.
This would be simpler if i don't have to check access_levels. My first idea is to make a very long IF(){}ELSEIF(){} statement for example.
if($this->auth['result'] = 1 ){
load view here.
}
is this acceptable? also, how would i go about it efficiently? and regarding the authentication, this part i'm really confused if i'm using it the right way or not.
Also if i ever do a very long if statement, what values would i check? like i know you have to check the access level for certain views but do i also check the username logged in and match it to the database if it's accesslevel is the same?
You can try using switch case
public function loadView()
{
switch($this->auth['result']){
case 1:
$view= "home";
break;
case 2:
$view= "userprofile";
break;
case 3:
$view= "hotelprofile";
break;
default:
$view= "Welcome";
break;
}
$this->load->view($view);
}
Related
I have a question more regarding style and organization that anything else. I often find myself having a single page (controller) that requires multiple ajax calls. Rather than creating a separate controller just for the AJAX calls because that would mean more controllers I just do the following:
class Management extends MY_Controller
{
public function __construct()
{
parent::__construct();
$this->protect->protect();
if ($this->uri->segment(2, 0) !== 0 && !$this->input->is_ajax_request()) {
exit('No direct script access allowed');
}
}
public function index()
{
$this->load->model('management_model');
$data['row_config'] = $this->management_model->getConfigRows();
$data['row_users'] = $this->management_model->getUsers();
$data['roles'] = $this->management_model->getRoles();
$this->tpl->head();
$this->load->view('management/scripts');
$this->tpl->body();
if ($this->messages->hasMessages()) {
$this->output->append_output($this->messages->display());
}
$this->load->view('management/manage', $data);
$this->load->view('management/current_users', $data);
$this->load->view('management/modals', $data);
$this->tpl->footer();
}
public function get_user_details()
{
$user = new \Auth\ASUser($_POST['userId']);
echo json_encode($user->getAll());
}
public function delete_user()
{
$user = new \Auth\ASUser($_POST['userId']);
$user->deleteUser(true);
}
As the index is the only page where I actually need to render a proper view, and the rest are ajax calls I just check the URI segment to see if something other than the index exists and then check if its an ajax request.
Is this bad practice? Should I separate the AJAX calls and the view controller?
Honestly, i don't think that there is a pattern on where you should add your ajax functions, especially in Codeigniter which is loosely coupled for most of it's structure.
In my opinion you should ask yourself the below, in order to find where you should place your ajax calls.
Are the returned data from the ajax call, related to the Controller i am already?
Will i ever use again the same method/functionality in another Controller or somewhere else?
Do i need the already defined constructor of the Controller, which i think i should place the ajax call?
Do i count on the DRY principle of software development?
Is my code flexible, reusable, etc.?
Each project has it's own philosophy and workflow. The design pattern and the data structure that you will decide you will follow, will solve most of your questions in your coding-style.
Your question is asking an opinion which is contrary to Stack Overflow's SOP. But I'll offer my opinion anyway.
Is this bad practice? I don't think so. That said, why make a controller larger than it needs to be? If your Ajax is handling the full CRUD functionality for some page then the controller could be quite large. You might be better served by a separate controller.
An Ajax call is a distinct request to the server - essentially the same as directing the browser to a separate page on some other browser tab. Why not make that request to a controller that is dedicated to Ajax? It could be argued that such a controller produces better "Separation of Concerns". That is good practice.
There is one technique to make certain this controller is only used when requested by an ajax call. A couple lines of code in the controller will make the determination.
class Some_ajax_handler extends CI_Controller
{
public function __construct()
{
if(!is_ajax()){
show_404();
}
parent :: __construct();
//if needed, do the rest of your construct here
}
public function get_user_details()
{
$user = new \Auth\ASUser($_POST['userId']);
echo json_encode($user->getAll());
}
}
The call to show_404(); ends with a call to exit() so you don't need a return; statement or else block in the controller. You can be confident that any method that gets called on this controller is indeed an ajax request.
On the other hand, if a controller's view uses Ajax to get the contents for a select input, or some other simple GET call, then creating a separate controller seems like overkill.
BTW, there is a CI library simplifying Ajax calls HERE that you might find interesting.
The one criticism I offer isn't about ajax but about your use of new which is contrary to the "Codeigniter way" for loading and utilizing other classes (libraries). But I guess that's a different topic.
I have the model:
class Task extends Model {
}
with some fields
protected $fillable = ['message', 'due_time', 'status', 'etc...'];
I've added custom function:
public function getEditableStateFor{AttributeName}
In my helper function I check that if
method_exists($class, 'getEditableStateForField1')
than I allow to edit this field depending on boolean value returned from this function.
Example:
if( ! $class->getEditableStateForField1() ) {
return "You can not edit field field1";
}
Here is how looks like some functions in Task:
private function isCreator() {
$user = Auth::user();
if($user) {
return $user->id === $this->creator_id;
}
return false;
}
public function getEditableStateForMessage() {
return $this->isCreator();
}
public function getEditableStateForDueTime() {
return $this->isCreator();
}
Is this a good way to do it or it is very bad design because of hidden dependency on Auth::user()?
What is a better way?
I do not want to put this logic inside controllers because this logic propagates to another models and is universal across application.
I'm like you and like to have Models that contain as much of the business logic as possible while remaining totally free of depencies on the "web" part of the application, which I believe should stay in Controllers, Request objects, etc. Ideally, Models should be easily usable from command line interfaces to the application, from within the Tinker REPL, and elsewhere while still guaranteeing data integrity and that business rules are observed.
That said, it seems the Laravel creators had slightly different ideas, hence the Auth facade being easily available in the model.
What I would likely do is add a parameter of type User to the getEditableStateFor series functions, and then in turn pass that parameter to isCreator ($user) and elsewhere. That also frees you up to be able to allow associated users to edit each other's Tasks if that ever became a desired feature in the future.
Edit: another, perhaps better or perhaps worse, is to have an instance method like setCurrentUser ($user) then use setFieldNameAttribute methods so that the controller doesn't have to check the editability of fields, keeping that the model's responsibility. Then you could call the getEditableStateFor methods, which now check for the current user set by the above method (maybe falling back to Auth::user() or throwing a helpful error), inside the setter.
I have such function in the controller
public function actionNext(){
$category = $this->getCategory();
$not_finished = $this->getQuestionFromCategory($category);
if(!empty($not_finished)){
$next_question_id = getNextQuestionId();
$this->updateNextQuestion();
}
else{
addNextCategory();
}
}
My question is: all fuctions
getCategory
getQuestionFromCategory
getNextQuestionId
updateNextQuestion
addNextCategory
from the example should be in model or controller too (all functions is the requests to the db).
Normally the function related to the db are in model, tipically the model extend the active record and for this contain also the related sql/schema/model related function. in your case is think the function getCategory and probably getQuestionFormCategory
The function related to service for support controller action are place in controller. in your case genNextQuestionId, updateNewQuestion, addNextQuestion.
The main rules is: what's regarding the structural knwoledge of an entity is in model, what's regarding tactical behaviuor is in controller.
Obviously the part related to sort and find are placed in ...search class.
I'm using the MVC PHP framework Codeigniter and I have a straight forward question about where to call redirect() from: Controller or Model?
Scenario:
A user navigates to www.example.com/item/555. In my Model I search the item database for an item with the ID of 555. If I find the item, I'll return the result to my controller. However, if an item is not found, I want to redirect the user somewhere. Should this call to redirect() come from inside the model or the controller? Why?
No your model should return false and you should check in your controller like so:
class SampleModel extends Model
{
//Construct
public function FetchItem($id)
{
$result = $this->db->select("*")->from("table")->where("item_id",$id)->get();
if($result->num_rows() == 0)
{
return false;
}
//return result
}
}
and within your controller do:
function item($id)
{
$Item = $this->SampleModel->FetchItem($id);
if(!$Item)
{
redirect("class/error/no_item");
}
}
Models are for data only either return a standard result such as an key/value object or a boolean.
all logic should be handled / controlled by the Controller.
Models are not page specific, and are used globally throughout the whole application, so if another class / method uses the model, it might get redirect to the incorrect location as its a different part of your site.
It seems like the controller would be the best place to invoke your redirect because the controller typically delegates calls to the model, view, or in your case, another controller.
However, you should use whatever makes the most sense for your application and for what will be easier to maintain in the future, but also consider that rules do exist for a reason.
In short, if a coworker were to try to fix a bug in your code, what would the "reasonable person" standard say? Where would most of them be most likely to look for your redirect?
Plus, you said you're returning the result to your controller already... perhaps that's where you should make your redirect...
I have a MVC application that receives an input from a form.
This is a login form so the only validation that is necessary is to check whether the input is non-empty.
Right now before I pass it to the model I validate it in the controller.
Is this a best practice or not? Does it belong to the model?
I don't think there's an official best practice limiting validation to any single part of the MVC pattern. For example, your view can (and should) do some up-front validation using Javascript. Your controller should also offer the same types of validation, as well as more business-logic related validation. The model can also offer forms of validation, i.e., setters not allowing null values.
There's an interesting discussion of this at joelonsoftware.
I have been thinking about this for a LONG time and after trying putting validation in both controllers and models.... finally I have come to the conclusion that for many of my applications... validation belongs in the model and not in the controller. Why? Because the same model could in the future be used by various other controller calls or APIs... and then I would have to repeat the validation process over and over again. That would violate DRY and lead to many errors. Plus philosophically its the model which interacts with the database ( or other persistent storage ) and thus is sort of a 'last call for alcohol' place to do this anyway.
So I do my get/post translation in the controller and then send raw data to the model for validation and processing. Of course I am often doing php/mysql web applications and if you are doing other things results may vary. I hope this helps someone.
Validation must be in the Model
Only the model knows the "details" of the business. only the model knows which data is acceptable and which data is not. the controller just knows how to "use" the model.
for example: let's say we need the functionality of registering new users to our system.
The Model:
public function registerUser(User $user){
//pseudo code
//primitive validation
if(!isInt($user->age)){
//log the invalid input error
return "age";
}
if(!isString($user->name)){
//log the invalid input error
return "name";
}
//business logic validation
//our buisnees only accept grown peoples
if($user->age < 18){
//log the error
return "age";
}
//our buisness accepts only users with good physique
if($user->weight > 100){
//log the error
return "weight";
}
//ervery thing is ok ? then insert the user
//data base query (insert into user (,,,) valeues (?,?,?,?))
return true;
}
Now the controller/s job is to "use" the model registerUser() function without the knowledge of how the model is going to do the validation, or even what is considered "valid" or not!
the Controller:
$user = new User();
$user->age = isset($_POST['age']) ? $_POST['age'] : null;
$user->name = isset($_POST['name']) ? $_POST['name'] : null;
$user->age = isset($_POST['weight']) ? $_POST['weight'] : null;
$result = $theModel->registerUser($user);// <- the controller uses the model
if($result === true){
//build the view(page/template) with success message and die
}
$msg = "";
//use the return value from the function or you can check the error logs
switch ($result){
case"age" :
$msg = "Sorry, you must be over 18";
break;
case "name":
$msg = "name field is not correct";
break;
case "weight":
$msg = "Sorry, you must have a good physique";
break;
}
//build the view(page/template) with error messages and die
The class user
class User {
public $age;
public $name;
public $weight;
}
having an architecture like that will "free" the controllers completely from the details of the business logic -which is a good thing-.
Suppose we want to make another form of user registration somewhere else in the website (and we will have another controller allocated for it). now the other controller will use the same method of the model registerUser().
But if we distributed the validation logic between the controller and the model they will not be separated -which is bad and against MVC- meaning that every time you need to make new view and controller to register new user you must use the same old controller AND the model together. Moreover if the business logic is changed (we now accept teenagers in our sports club) you are going only to change the code in the model registerUser() function. the controllers code is still the same.
Its business logic, so no, it doesn't belong in the model.
Within the controller you have the ModelState property to which you can add validation errors to.
See this example on the MSDN.
Assuming your application is structured like:
Model-View-Controller
Service
Persistence
Model
The user input would come to your controller, and you would use services in the service layer to validate it.
Business Logic -> Controller
Data Validation -> Model