Good Morning everyone. Recently i've read an article about mvc pattern saying that most of the php frameworks out there implemented the mvc pattern wrong.
php master mvc pattern part 1
php master mvc pattern part 2
Well after reading this and looking over that implementation a question appeared.
How on earth would call in the view the method called in model? What i'm trying to say is this.
This is a piece of code from the article.
<?php
$model = $_GET['model'];
$view = $_GET['view'];
$controller = $_GET['controller'];
$action = $_GET['action'];
if (!(empty($model) || empty($view) || empty($controller) || empty($action))) {
$m = new $model();
$c = new $controller($m, $action);
$v = new $view($m);
echo $v->output();
}
let's say we've written a small implementation of this design pattern after reading the article and we have the following code:
<?php
class Index extends Controller
{
public function __construct(IndexModel $model, $action)
{
$this->model = $model;
}
public function someAction($id)
{
$this->model->getData($id);
}
}
class Index extends View
{
public function __construct(IndexModel $model, $action)
{
$this->model = $model;
}
public function someAction()
{
$this->model->getData();
}
}
class Index extends Model
{
public function __construct()
{
//Some Code Here
}
public function someAction()
{
// Inserting Data into database.
}
}
As you can see we are calling the same method both in controller and in view to get the data from the database. But if i know correctly the view should take care of the controller's job so the $id wouldn't be right to parse it again in the view or something like that. Then how this can be solved?
There's no 1:1 correlation between controllers and parts of the model (note: not "models" but parts of _the model_). Just because you have an "index controller" doesn't mean you need an "index model" and "index view".
The M, V and C are not constructed together. The controller is constructed, and then it decides which model method to load/construct/invoke and which view should respond to the request.
There's discussion about whether the controller should invoke the view, or whether the model should "update" the view. Since web requests are ephemeral and there's no "constant" view, the latter makes little sense in PHP; it's more appropriate for the original SmallTalk or Obj-C or similar environments.
The first code snippet has terrible use of empty (see here) and, again, should not construct all parts together.
MVC should be approached like this:
The model is the app. It's not just a "data handler" or "data store", it is the core app. Everything the app does, business logic wise, is "the model". This includes sending email notifications, database maintenance work and such auxiliary things, they're all in the model.
The view is responsible for producing output of various forms. The view should be able to interact with the model to get the data it needs to do its job. Data should not be "pushed into" the view, the view should be able to "pull" the data it needs; otherwise something external to the view needs to know what data the view needs, which means the view logic is not self contained in the view anymore.
The controller is just the little bit of glue that's left to invoke the correct model method and view in response to the incoming request.
I typically structure these parts like this:
The model consists of various parts, including data storage handlers, "primitives" (classes that model individual business objects) and "services". The "services" contain all the "actions" your app can do and form the model API. Whenever you want to "do" something in the app like "register a user" or "fetch all records for date range X through Y", there's one specialized method for it in the service API. Just looking at this service API you should be able to enumerate all the things your app "does".
There's one object which is either a "dispatcher" or a "service locator" or simply a dependency injection container that simplifies instantiation of these service classes and allows someone to call them. The controller gets one of these and so does the view.
There's a router which does some "rough routing" based on the URL, invoking a controller method. The controller further looks at the details of the request and decides to call a model method and/or to respond with a view.
The view may decide the best way to present some data based on the specifics of the request, e.g. whether to respond with an HTML page or a JSON data blob. Yay for RESTful services.
In rough pseudo code:
$r = new Router;
$r->route($_GET, $_POST, $_SERVER); // or something like that
This dispatches to something like:
class FooController {
public function __construct(ServiceLocator $services) { ... }
public function bar(Request $request) {
$request->assertIsPost();
$this->services->locate('Baz')->froogleTheWibbles($request->getPostParams());
(new BarView($this->services))->displayWibbles($request);
}
}
class BarView {
public function __construct(ServiceLocator $services) { ... }
public function displayWibbles(Request $request) {
switch ($request->accepts()) {
case 'html' :
$this->loadTemplate(...);
...
case 'json' :
echo json_encode($this->services->locate('Baz')->getWibbles());
}
}
}
And the model does whatever it needs to do...
class Baz {
public function froogleTheWibbles(array $data) {
foreach ($data as $wibbleData) {
$wibble = new Wibble($wibbleData);
$this->wibbleStore->save($wibble);
}
...
}
}
There is no "one answer" for MVC, the important part is that the model contains everything your app "does" independent of input and output, the view can produce the right output as requested as independently as possible and the controller is just the little bit of glue that handles input conditions. There are various ways in which this can be realized. The important design principle should be the realization that the view and controller are interchangeable to fit different conditions (web page, JSON API, XML API, SOAP API, CLI invocation, ZeroMQ node etc.), but "the model" is not.
Related
I'm trying to understand how passing variables work exactly.
I've set a controller:
class indexController
{
public function calling()
{
$route = new Route():
$route->title = 'Register user';
$route->getview('users','register');
}
}
And a model:
class Route
{
public function getview($module,$filename)
{
require_once('templates/'.$module.'/'.$filename.'.phtml');
}
}
And a view file which has something like this:
<div class="title"><?php echo $this->title; ?></div>
How to set title for the view? Should I 'public' this var in controller, and get it while constructing model to use in my view file?
There are problems with your design but forgetting those this is a way you could do it.
public function var( $var, $val ) {
$this->vars[$var] = $val; // should disallow _view as a $var
}
public function getview($module,$filename)
{
$_view = 'templates/'.$module.'/'.$filename.'.phtml';
extract( $this->vars ); //creates variables for
require_once( $_view );
}
Use it like this
$route = new Route():
$route->var( 'title', 'Register user' );
$route->getview('users','register');
What gave you an impression that you are implementing MVC? Because from where stand, it looks like you have mixed up the responsibilities for each part of the pattern.
Here are the basics ..
MVC is an architectural design pattern, that is an expression of SoC principle. It separates the model layery (that is responsible for implementing domain business logic) from presentation layer. And within presentation layer it separates the parts that handle user input (controller) from logic that generates the user interface (view).
When applying this pattern to Web, the flow of information goes something like this:
controller receives request from users
controller alters the state of model layer and (maybe) the current view
view request necessary information from model layer
view produces a response to the user
What you have there is NOT a view, but simply a template. And what you have there is NOT model, but simply a class.
And now you questions:
How to set title for the view?
Your view should request the information, that it needs, from model layer:
namespace Views;
class Doclument
{
// ... some code
public function foobar()
{
$library = $this->serviceFactory->acquire('Library');
$title = $library->getCurrentDocument('title');
$content = $library->getCurrentDocument('content');
$this->template['main']->assign([
'title' => $title,
'body' => $content,
]);
}
// ... some more code
public function render()
{
/*
if any templates have been initialized,
here you would put code for combining them and
return html (or some other format)
*/
}
}
Off course, you would need to know, which document the user wanted to see .. that should be done in a controller:
namespace Controllers;
class Document
{
// ... again, some code, that's not important here
public function getFoobar( $request )
{
$library = $this->serviceFactory->acquire('Library');
$library->useLanguage( $request->getParameter('lang') );
$library->locateDocument( $request->getParameter('id') );
}
}
The $serviceFactory would be shared between both controllers and views, since it's how you interact with the model layer. This also gives you a way to initialize each service only once, without creating a dependence on global state.
Should I 'public' this var in controller, and get it while constructing model to use in my view file?
No.
Model layer (yes, it should be layer and not a class) should not be aware of anything from implementation of presentation layer. An actually, view should not be aware of controller's either.
Actually, having public variables in OOP is considered to be a bad practice (unless you are creating data structures .. think: stuff like binary trees). It causes your code to leak encapsulation.
P.S.
I'm trying to understand how passing variables work exactly.
That's basics of OOP. You should not be playing around with high-level constructs like MVC pattern, if you do not have a good grasp on OOP concepts, practices and methodology.
Look up thing called "dependency injection".
I realize this topic has been asked and addressed repeatedly, and although I've read through countless similar questions and read through countless articles, I'm still failing to grasp a few key concerns... I'm attempting to build my own MVC framework for learning purposes and to better familiarize myself with OOP. This is for personal private use, not to imply that as an excuse for being lazy, but rather I'm not so concerned with having all the bells and whistles of more robust frameworks.
My directory structure is as follows:
public
- index.php
private
- framework
- controllers
- models
- views
- FrontController.php
- ModelFactory.php
- Router.php
- View.php
- bootstrap.php
I have an .htaccess file with directs all requests to index.php, this file contains basic configuration settings such as timezone and global constants, and it then loads the bootstrap.php file. The bootstrap contains my autoloader for classes, starts the session, defines global functions to use throughout my project, and then calls the router. The router picks apart the request from the URL, validates it using a ReflectionClass, and executes the request in the form of example.com/controller/method/params.
All of my controllers extend the FrontController.php:
<?php
namespace framework;
class FrontController
{
public $model;
public $view;
public $data = [];
function __construct()
{
$this->model = new ModelFactory();
$this->view = new View();
}
// validate user input
public function validate() {}
// determines whether or not a form is being submitted
public function formSubmit() {}
// check $_SESSION for preserved input errors
public function formError() {}
}
This front controller loads the ModelFactory:
<?php
namespace framework;
class ModelFactory
{
private $db = null;
private $host = 'localhost';
private $username = 'dev';
private $password = '********';
private $database = 'test';
// connect to database
public function connect() {}
// instantiate a model with an optional database connection
public function build($model, $database = false) {}
}
And base View:
<?php
namespace framework;
class View
{
public function load($view, array $data = [])
{
// calls sanitize method for output
// loads header, view, and footer
}
// sanitize output
public function sanitize($output) {}
// outputs a success message or list of errors
// returns an array of failed input fields
public function formStatus() {}
}
Finally, here is an example controller to demonstrate how a request is currently processed:
<?php
namespace framework\controllers;
use framework\FrontController,
framework\Router;
class IndexController extends FrontController implements InterfaceController
{
public function contact()
{
// process form if submitted
if ($this->formSubmit()) {
// validate input
$name = isset($_POST['name']) && $this->validate($_POST['name'], 'raw') ? $_POST['name'] : null;
$email = isset($_POST['email']) && $this->validate($_POST['email'], 'email') ? $_POST['email'] : null;
$comments = isset($_POST['comments']) && $this->validate($_POST['comments'], 'raw') ? $_POST['comments'] : null;
// proceed if required fields were validated
if (isset($name, $email, $comments)) {
// send message
$mail = $this->model->build('mail');
$to = WEBMASTER;
$from = $email;
$subject = $_SERVER['SERVER_NAME'] . ' - Contact Form';
$body = $comments . '<br /><br />' . "\r\n\r\n";
$body .= '-' . $name;
if ($mail->send($to, $from, $subject, $body)) {
// status update
$_SESSION['success'] = 'Your message was sent successfully.';
}
} else {
// preserve input
$_SESSION['preserve'] = $_POST;
// highlight errors
if (!isset($name)) {
$_SESSION['failed']['name'] = 'Please enter your name.';
}
if (!isset($email)) {
$_SESSION['failed']['email'] = 'Please enter a valid e-mail address.';
}
if (!isset($comments)) {
$_SESSION['failed']['comments'] = 'Please enter your comments.';
}
}
Router::redirect('contact');
}
// check for preserved input
$this->data = $this->formError();
$this->view->load('contact', $this->data);
}
}
From what I'm able to understand, my logic is off for the following reasons:
Validation should be done in the Model, not the Controller. However, a Model should not have access to $_POST variables, so I am not entirely sure whether or not I'm doing this part correctly? I feel like this is what they call a "fat controller" which is bad, but I'm not sure what needs to change...
The controller should not send data to the View; instead, the View should have access to the Model to request its own data. So would moving the $data property out of the FrontController and into the ModelFactory, and then calling the View from the Controller without passing along data resolve this issue? Technically it would then adhere to the MVC flowchart, but the proposed solution seems like such an insignificant or even trivial detail, assuming it's that simple, which it probably isn't..
The part that has me questioning my whole implementation is that I have a User object that is instantiated with a users corresponding roles and permissions, and I've been trying to figure out how or more specifically where to create an isAllowed() method that can be called from both the Controller and View. Would it make sense then to put this method in the Model, since both the Controller and View should have access to the Model?
Overall, am I on the right track here or what glaring problems do I need to address in order to get on the right track? I'm really hoping for a personal response specific to my examples rather than a "go read this".. I appreciate any honest feedback and help.
The $_POST superglobals should be abstracted by a request instance, as explained in this post.
Input validation is not the responsibility of the controllers. Instead it should be handles by domain objects within model layer.
Model factory is no a model.
Defining class parameter visibility as public breaks the object's encapsulation.
HTTP location header (redirect) is a form of response. Thus, it should be handled by view instance.
In its current form, your controllers are directly manipulating superglobals. This causes tight coupling to the globals state.
Authorization checks should be performed outside controller. Not inside of it.
Your "model factory" should instead be a service factory, that is injected in both controller and view. It would ensure that each service is instantiated only once and thus let your controllers work with same model layer's state.
First, I think it's great you are trying to create you own framework. Many say that everyone should do this, if only for learning purposes.
Second, I would suggest you read this Wikipedia article on frameworks. Many people don't realize there are different patterns for routing (url dispatch, traversal), and views (push, pull).
Personally, I don't see a need to abstract out the super globals since they are already abstractions (by php) from the raw input (php://input) and can be modified. Just my opinion.
You are right that validation should be done by the Model. You don't validate forms, you validate data. As for the View accessing the data, that depends on the pattern you choose. You can push the data to the View, or the View can pull the data.
If you are curious, my attempt at a MVC basic framework is on github. Its 4 files, less 2K line of code (DB layer is 1K lines). It implements traversal (component) routing and pulls data, there were already plenty of frameworks that implement the alternate patterns.
Validation should be done in the Model, not the Controller. However, a
Model should not have access to $_POST variables, so I am not entirely
sure whether or not I'm doing this part correctly? I feel like this is
what they call a "fat controller" which is bad, but I'm not sure what
needs to change...
That is right, Model should know nothing about the request, so, you need to pass $_POST to the models, but it will not know that are request parameters.
One thing: validation that has nothing to do with business logic should stay in controller. Let's say, you create a CSRF token for your forms for security reasons, this valitadion should be inside the controller, because it handles requests.
The controller should not send data to the View; instead, the View should have access to the Model to request its own data. So would moving the $data property out of the FrontController and into the ModelFactory, and then calling the View from the Controller without passing along data resolve this issue? Technically it would then adhere to the MVC flowchart, but the proposed solution seems like such an insignificant or even trivial detail, assuming it's that simple, which it probably isn't..
That's not necessarily true. This approach is called Active Model, and normally you use the Observer pattern, with models being observed by the views. If some model changes, it notifies the view that will update itself. This approach is more suitable for desktop applications, not web-based ones. In web applications, the most common is to have controllers as intermediaries between model and view (Passive Model). There's no right approach, you should choose the one you like the most.
The part that has me questioning my whole implementation is that I have a User object that is instantiated with a users corresponding roles and permissions, and I've been trying to figure out how or more specifically where to create an isAllowed() method that can be called from both the Controller and View. Would it make sense then to put this method in the Model, since both the Controller and View should have access to the Model?
Well, this one there's no way, I'll have to tell you to read about ACL.
I would like to implement controllers that connect to any specific views like MVC does. I'm not using any framework that provided in PHP.
So, I need some guide and advice on doing it.
I have some controllers and views. For my views,i would like to just output my data only.
My concern now is how my function (like create() ) in controllers, can get all the $_POST['params'] that users input data in my views/create.php, and create a new Model in the create() controllers's function.
So,right now, i'm thinking to do in this way, I will create MyViews class in my controllers folder. The purpose is loading the specific views and get all the $_POST params into an object. Then, every controllers like Users_controllers, will create MyViews. In the function of Users_controllers, like create(), destroy(), I might use the function in MyViews to load specific views to load the object.
I found a source that load views
<?php
class MyView {
protected $template_dir = 'templates/';
protected $vars = array();
public function __construct($template_dir = null) {
if ($template_dir !== null) {
// Check here whether this directory really exists
$this->template_dir = $template_dir;
}
}
public function render($template_file) {
if (file_exists($this->template_dir.$template_file)) {
include $this->template_dir.$template_file;
} else {
throw new Exception('no template file ' . $template_file . ' present in directory ' . $this->template_dir);
}
}
public function __set($name, $value) {
$this->vars[$name] = $value;
}
public function __get($name) {
return $this->vars[$name];
}
} ?>
hmm,I have no idea How I can detect the _POST params
if(isset($_POST['Post']))
{
$model->attributes=$_POST['Post'];
if($model->save())
$this->redirect(array('view','id'=>$model->id));
}
this is the Yii framework I observed. How could I detect params whether is $_POST or $_GET after load a specific views.
Any guidance and advice to archive my tasks?
Unrelared to question You have one major problem: your ability to express what mean is extremely limited. The question, which you asked, was actually unrelated to your problem.
From what I gather, you need to detect of user made a POST or GET request. Do detect it directly you can check $_SERVER['REQUEST_METHOD'], but checking it withing controller might be quite bothersome. You will end up with a lot of controller's methods which behave differently based on request method.
Since you are not using any of popular frameworks, is would recommend for you to instead delegate this decision to the routing mechanism.
A pretty good way to handle this, in my opinion, is to prefix the controller's method names with the request method: postLogin(), getArticles() etc. You can find few additional example here. If there is a POST request, it will have something in $_POST array.
What are calling "views" are actually templates. If you read this article, you will notice, that the code there is actually an improved version of your MyView. Views are not templates. Views are instances which contain presentation logic and manipulate multiple templates.
P.S. If you are exploring MVC and MVC-inspired patterns in relation to PHP, you might find this post useful.
I'm using the MVC pattern in my application.
Now I need the view object in a model.
I don't want to add the view as a parameter for my function in the model (since I need it in other functions as well). And I don't want to keep on passing it.
Should a add the view as an attribute for the constructor of the model?
Is there another way? Shouldn't I be needing the view object in the model in the first place?
What would be the preferred way of doing it?
Example:
Controller
function someAction()
{
$somemodel->add();
}
Model
class SomeModel()
{
function add()
{
if ($view->user) {
// do stuff
$this->mail();
} else {
// do other stuff
}
}
function mail()
{
Mailer::send($view->user->email, $this->getitems(), $view->layout);
}
function getitems()
{
return Items::getitems($view->user);
}
}
If you're really doing MVC, then you won't need the view in the model, because only the controller should have access to the view.
Looking at the code you've provided, I can tell one thing: the add() method should not reference $view in any way (even for accessing its properties). Instead, the model should be provided with the $view->user value from the controller. The same goes for the mail() method.
Consider fixing those issues. Otherwise, you'll get into something worse later on.
The model should be separate from the view. So, as mkArtak said, the controller should be the only thing that communicates with the view. Which then passes only the necessary information to the model.
As for the model, it should really only deal with the information that it understands.
i.e. if you had a Car model... you don't want to build it dependent on it's factory. If you did, you would have to change your code if you wanted to build it in different factory.
The controller is where you 'bake' everything prepare for render. By bake I mean you consider any passed in $_REQUEST params, make model API calls to get the data you need, and set template variables to be rendered. Your action, at the end of this process should make a call to a template (view) you choose in order to render the 'baked' template variables.
EDIT: Previous title, because no one was reading the question: "If I'm making AJAX calls in an MVC framework, is it common to have 'getter' controller methods over the model?"
It's all in the title: If I want to make an AJAX call to delete a user, that DB code clearly lives in the model $this->userModel->delete($id).
If I'm making all of the CRUD calls via AJAX, do I just have passthrough controller methods to expose those model calls to a URL?
function delete($id) {
$this->userModel->delete($id);
}
etc? This seems silly, but it also makes sense... but it also makes me feel like I'm missing something. Is this the most common pattern?
Thanks!
When it comes down to Ajax under the MVC Framework I usually tend to have each function with a specified keyword such as fetch / set.
something like this
class Users extends Controller
{
public function Fetch($id){}
public function Create(){}
public function Update($id){}
public function Remove($id){}
}
To answer your question: yes
The task of the controller is the decision maker, so that you would perform authentication checks etc within the controller for security purposes.
Think of it this way, you would not use the same controller for changing records in your administration as you would within your users front-end, but you would use the same models.
the models should be used in more places than just the front end, so you would not place a session check, input validation in the model methods as you would perform different checks depending on the location the action is taking place.
Your frontend controller would have something along the lines of:
public function Fetch($id)
{
if($this->session->get_userdata("auth_level") & USER_AUTH_LEVEL_READ)
{
//Show data
}
}
where as your administration would have:
public function Fetch($id)
{
if($this->session->get_userdata("auth_level") & IS_ADMINISTRATOR)
{
//Show data
}
}
if you place the very same check in your models then you would have generate several models that would do return the same data regardless of the location.