This question already has answers here:
How should a model be structured in MVC? [closed]
(5 answers)
Closed 9 years ago.
In the ideal world one should not rely on singletons, the model in the controller and model inside the view would be 2 different instances. The problem arises when the controller sets a state and the view presentation depends on that state. For example:
class MyController extends Controller {
public function __construct(ModelUsers $cModel)
{
$this->model = $cModel;
}
public function action_Search($username) {
$this->model->filterByUsername($username);
}
}
class MyView extends View {
public function __construct(ModelUsers $vModel)
{
$this->model = $vModel;
}
public function users() {
return $this->model->getUsers();
}
}
How to share data between the controller model and the view model?
Starting from basics
A view requests from the model the information that it needs to generate an output representation to the user.
It means the view should be only responsible for showing the information. Just for that. You can also do some things like triming, chaning text size etc. but you shouldn't do some countings there or more complicated operations.
A model notifies its associated views and controllers when there has been a change in its state. This notification allows the views to produce updated output, and the controllers to change the available set of commands.
Model should be responsible for doing data operations. You can use it for example to get the records from database. It just be responsible for data handling.
A controller can send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document). It can also send commands to the model to update the model's state (e.g., editing a document).
Controler is kind a proxy between model and view. You get there params and according to this params you set proper action of your controller. This action should create correct model object and use it to get data then assign to the view.
I've never used singleton in models. If you need some classes that would help MVC structure you can use helpers and as Hast suggested Registry pattern. I'm not a fan of using singleton.
You may also want to look at When to use singleton
So your question.
Controler -> model = Passing data via arguments of model's methods
Model -> controler = If reference then just work on it, if argument then do something and return result
Controler -> view = assign proper data to be viewed.
View->controller = go to special url to make data or use ajax request to retrieve it.
You can use Registry or Dependency injection instead.
Also in some cases you may pass some data to your view class as array. Like this:
class MyView extends View {
private $data = array();
public function __construct($data)
{
$this->data = $data;
}
public function users() {
return $this->data['model']->getUsers();
}
}
Of course you have to pass model when you caling the View class from your controller (or wherever you make call).
Related
#1
Class controller {
$customer = Model\customer::find($id);
}
#2
Class controller {
$customer = new Model\customer;
$customer = $customer->data($id);
}
Class Model {
public function data($id) {
return self::find($id);
}
}
I'm new in larava, I got a question about MVC query.
I saw many people put query inside of controller like #1
I usually like to put query inside of model class.
any suggestion?
According to me MVC means - Model View Controller.
In a general language Model should contain the Database Query related stuffs whereas in View you should always use variables to populate view data and at last Controller is generally for catching the requests and send the response by doing some logical functionality on request.
<?php
namespace App\Models;
class ExampleModel
{
// All the database related methods like - all, paginate, find, where, etc would goes in the model...
}
In case of Views
<html>
...
YOUR_CONTENT_HERE
...
</html>
and in case of Controller,
<?php
namespace App\Controllers;
class ExampleController
{
public function index()
{
$request = request()->all();
... do some logical work here ...
return response($data);
}
}
Thats what MVC code structure would be like - in my opinion
Hope this helps!
It's ok to use the model's find() method directly to retrieve an entity.
If you are developing a larger application, you should, however, consider moving all the logic into your own classes. The controller should just collect the input from the http request, session etc. and call a method on a service to do the actual work. That way your code is not glued to a controller action or route.
My goal of asking this question is to ferret out whether there are benefits to injecting Controller directly with the data it needs (more specific approach) opposed to injecting a Model into a Controller (more generic approach). Or to establish whether or not it is just a matter of preference.
Injecting Controller with Model:
Model can be used to run all kinds of queries to retrieve various bits of data, but it is a heavier-weight construct than the data itself. Model essentially contains data, or at least it can access all the data you may need. Example:
class CategoryControllerWithModel
{
private $model;
public function __construct($model)
{
$this->model = $model;
}
// generates HTML for input form
public function genHtml()
{
/* retrieve data */
$categories = $this->model->getCategories();
//...
}
}
//instantiation within Factory Method:
class Factory
{
$model = new CategoryModel();
$controller = new CategoryControllerWithModel($model);
return $controller;
}
Injecting Controller with Data:
Here we do a bit more upfront with in the Factory method but we get a leaner Controller that only receives exactly the data it needs and is so completely separated from the Model that it is not even aware of its existence.
class CategoryControllerWithData
{
private $categories;
public function __construct($categories)
{
$this->categories = $categories;
}
public function genHtml()
{
$categories = $this->categories;
}
}
//instantiation within Factory Method:
class Factory
{
$model = new CategoryModel();
//a bit more work to get the data Controller needs
//benefit: Controller not tied to the Model
$categories = $model->getCategories():
$controller = new CategoryControllerWithData($categories);
return $controller;
}
Question:
I suppose MVC stands for exactly that -- Model, View, Controller, so injecting Model is probably considered to be an "okay" thing to do. If so, am I taking this too far by trying to remove Controller dependency on Model?
Suppose I insist that I want to inject Data into my Controllers rather than the Model. Is this a purely preferential issue do you see any concrete benefits of doing so?
From my point of view, Factory shouldn't be responsible for domain logic. It should only be responsible for building things up.
In this case, where you are injecting data, Factory has to know what categories controller is searching for, are there any filtering and so on.
So I think for controller you should only inject model, keep Factory single responsibility only for building things and controller should be responsible for it's data.
I think it's a matter of "separation of concerns" also I do not think that would be a good example of using MVC. I would think more along these lines:
class FooController
{
public function actionView($alias){
$category = Category::loadByAlias($alias);
..... load and render layouts etc .....
}
public function actionList(){
$categories = Category::loadAll();
..... etc ......
}
}
like this the neither the Controller nor the Factory need to know what needs to be done when you load a category nor do they have to handle active/inactive status, even User access ... etc this is all Model Logic, Model can have beforeLoad and afterLoad functions, conditions for listing all categories, eager or lazy loading of related models etc...
Is it possible to automatically bind an instance of a model to a parameter in a controller action? Is their any workaround for doing this if it does not already exist within Yii itself?
I know this is possible in Laravel and ASP.NET MVC. Here is what I want to achieve:
class PostController extends Controller {
public function actionEdit(Post $post) {
if($_POST['Post']){
$post->attributes = $_POST['Post'];
$post->save();
}
$this->render('edit', array('post'=>$post));
}
}
Given a url like localhost/?r=post/edit&post=1
[eg Yii::app()->createUrl('post/edit',array('post'=>$mypost->id))] the id 1 is converted to an instance of CActiveRecord [i.e. Post::model()->findByPk(1) is called automatically]
from your snippet it seems you are trying to pass both a $_POST and $_GET variable to your action.
Values in $_POST,$_GET are stored as associated arrays not objects themselves.
In Yii you can't do that directly load the model, however you can achieve your objective like this.
class PostController extends Controller {
public function actionEdit($post) {
$postModel = Post::model()->findByPk($post)
$this->render('edit', array('post'=>$postModel));
}
}
This is slightly longer but more useful if you want to bind additional conditions for example allow editing only is post is not deleted, or is active etc. You can use then findByAttributes, or findAll with additional conditions which may be implicit and not necessarily passed as parameter
Alternatively if you really required such a functionality you can write a component class with a custom action, where you can load a model as you wish
<?php
class LoadModel extends CComponent {
public function loadModel($type,$id){
if(!in_array($type,$arrayofpossibleModels || !is_numeric($id)){ //Validation if $type is a model name is required.
throw new CHttpException("400","Bad Request")
}
return $type::model()->findByPk($id);
}
}
I am working on building a lightweight MVC, mainly for the learning process but I would like it to be good enough to use eventually.
Below is a basic example/demo of how a basic controller might would look, let's assume the URI has been processed and routed to this controller and these 2 methods.
1) I need to get data from database/cache/etc... inside my Model classes, I just need help on how I should load my models into my example controller below, you can see that I have added this below $profileData = $this->model->getProfile($userId) that is just made up and does not exist's, how could I get something like that to work though? Or should I load the model into the class a different way?
2) A lot of pages will require a user to be logged into the site. SHould I process that part below in the controller to check if a user is logged in, example, before building the profile page, check if user is logged in, if not then build a login page instead and add these checks inside of each controller method/page?
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
//GET data from a Model
$profileData = $this->model->getProfile($userId);
$this->view->load('userProfile', $profileData);
}
// domain.com/user/friends/
function friends()
{
//GET data from a Model
$friendsData = $this->model->getFriendlist();
$this->view->load('userFriends', $friendsData);
}
}
core
abstract class Core_Controller {
protected $view;
protected $model;
function __construct(DependencyContainer $dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
}
There are probably tons of ways to accomplish what you are trying.
The "easiest" is probably to just override the constructor and instantiate the model directly.
in User_Controller:
public function __construct(DependencyContainer $dc) {
parent::__construct($dc);
$this->model = new User_Model();
}
I'm guessing that you are looking for something a little more automated though. If you want the Model to have the same name as the controller minus "_Controller", just use get_class($this) in the constructor and use PHP's string functions to parse out what you want. Once you have that in a variable, you can use that variable to instantiate the model:
in Core_Controller:
public function __construct(DependencyContainer $dc) {
$this->view = new Core_View();
// $model_class should be 'User_Model' now
$model_class = str_replace('_Controller', '_Model', get_class($this));
// now instantiate the model
$this->model = new $model_class();
}
I haven't actually worked with any framework that can only have one model associated with each controller (except may CakePHP? I can't remember). With Symfony, the models and controllers are completely decoupled so you can use any model with any controller. You just instantiate the model as need. Symfony use the Doctrine ORM so for example, in a controller action, if you needed a model you would do something like this:
$model = Doctrine::getTable('User');
It might be worthwhile to consider a design more like that in order to promote a decoupled design and I promise that you will want more than one model in some controller at some point.
2.) As far as authentication. Something that seems to be fairly common is to have some sort of setting (whether in a config file or a member variable) that says whether or not the current action needs the user to be authenticated. This is processed each time the action runs (Yii calls these kinds of things filters). If the user needs to be logged in, it stores the page that they are trying to access, and then redirects them to a log in page (you should only ever have to create one). Once they properly authenticate, it will redirect them back to where they were originally heading.
I have a controller/model for projects. so this controls the projects model, etc, etc. I have a homepage which is being controlled by the pages_controller. I want to show a list of projects on the homepage. Is it as easy as doing:
function index() {
$this->set('projects', $this->Project->find('all'));
}
I'm guessing not as I'm getting:
Undefined property: PagesController::$Project
Can someone steer me in the right direction please,
Jonesy
You must load every model in the controller class by variable $uses, for example:
var $uses = array('Project');
or in action use method
$this->loadModel('Project');
In my opinion the proper way to do this is add a function to your current model which instantiates the other model and returns the needed data.
Here's an example which returns data from the Project model in a model called Example and calls the data in the Example controller:
Using Project Model inside Example Model:
<?php
/* Example Model */
App::uses('Project', 'Model');
class Example extends AppModel {
public function allProjects() {
$projectModel = new Project();
$projects = $projectModel->find('all');
return $projects;
}
}
Returning that data in Example Controller
// once inside your correct view function just do:
$projects = $this->Example->allProjects();
$this->set('projects', $projects);
In the Example view
<?php
// Now assuming you're in the .ctp template associated with
// your view function which used: $projects = $this->Example->allProjects();
// you should be able to access the var: $projects
// For example:
print_r($projects['Project']);
Why is this "better" practice than loading both models into your controller? Well, the Project model is inherited by the Example model, so Project data now becomes part of the Example model scope. (What this means on the database side of things is the 2 tables are joined using SQL JOIN clauses).
Or as the manual says:
One of the most powerful features of CakePHP is the ability to link relational mapping provided by the model. In CakePHP, the links between models are handled through associations.
Defining relations between different objects in your application should be a natural process. For example: in a recipe database, a recipe may have many reviews, reviews have a single author, and authors may have many recipes. Defining the way these relations work allows you to access your data in an intuitive and powerful way. (source)
For me it's more reasonable to use requestAction. This way the logic is wrapped in the controller.
In example:
//in your controller Projects:
class ProjectsController extends AppController {
function dashboard(){
$this->set('projects', $this->Project->find('all'));
}
$this->render('dashboard');
}
Bear in mind that you need to create dashboard.ctp in /app/views/projects of course.
In the Page's dashboard view (probably /app/views/pages/dashboard.ctp) add:
echo $this->requestAction(array('controller'=>'projects', 'action'=>'dashboard'));
This way the logic will remain in the project's controller. Of course you can request /projects/index, but the handling of the pagination will be more complicated.
more about requestAction(). but bear in mind that you need to use it carefully. It could slow down your application.