Passing data from MVC Controller to View in PHP - php

I have my own hand-rolled PHP MVC framework for some projects that I'm working on. When I first created the framework, it was in the context of building an admin CMS. Therefore, there was a very nice one-to-one relationship between model, view, and controller. You have a single row in the DB, which maps to a single model. The controller loads the model and passes it to the view to be rendered (such as into an edit form). Nice, clean, and easy.
However, now that I'm working on the front end of the site, things are getting sticky. A page isn't always a view of a single model. It might be a user directory listing with 20 users (each a User model). Furthermore, there might be metadata about the request, such as pagination (current page, total pages, number of results) and/or a search query.
My question is, what is the cleanest way to pass all this data to the view?
Some options I'm considering:
Have the controller create an array and pass that to the view as a single parameter:
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$data = array()
$data['models'] = $models;
$data['currentPage'] = $current;
$data['totalPages'] = $total;
return $view->render($data);
}
}
class UserView{
public function render($data){
// render the data
}
}
Create properties in the view class and have the controller populate them:
class UserView{
public $models;
public $currentPage;
public $totalPages;
}
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$view = new UserView();
$view->models = $models;
$view->currentPage = $current;
$view->totalPages = $total;
return $view->render();
}
}
Give the view some sort of generic HashMap or Collection object as a container which can hold any arbitrary number and name of data.
class UserView{
public $collection = new Collection(); // works like a Java collection
}
class UserController{
public function renderView(){
// assume there's some logic to create models, get pagination, etc.
$view = new UserView();
$view->collection->add($models,'models');
$view->collection->add($currentPage,'currentPage');
return $view->render();
}
}
I know that technically any of the could work, but I'm unsure of the best choice, or if there's a better or more conventional choice that I'm missing.

I'm going to recommend the concept of Fat Models, Skinny Controllers (or, Fat Models Thin Controllers if you prefer...)
In otherwords, your model is too strict - tying your model to represent only something like a RowDataGateway is extremely limiting.
In fact, I think good models hide the fact that you're reading the data from a database at all. Because, in reality, your data could be in text files, or from a web service, or whatever. If you treat your Model like nothing more than a glorified DBAL, you doom yourself to having tightly-coupled code in your controllers that just won't let you break away from the "data only comes from the database" way of thinking.

I've seen both of the first two methods implemented in popular MVC/templating frameworks.
django uses the first method, passing to the view a dictionary of variables which the view uses to fill the template.
smarty uses the second method, creating a Smarty object and assigning values to each the properties in the container.
Your third method seems to essentially be the same as the second, with minor architecture differences.
Really, I guess I haven't said anything that you haven't thought of already. Basically, these are all sounds ideas, so implement whatever you feel you are most comfortable with.

In the one I use, it has automatically has a view property in the controller that you can access methods and properties on the view. All public properties are then accessible within the view view '$this' since the view is rendered in it's own objects context.
In the controller:
$this->view->myVar = 'test';
And in the view:
$this->myVar; // 'test'
The same goes for the layout since the are both separate instances of the same view object:
$this->layout->myVar = 'test';
And then in the layout:
$this->myVar; // 'test'
The framework used to be proprietary, but is about to be released to the general public. I'd be happy to send you some code from it if you think that'd help. Remember, the simplest answer is usually the best answer.

Related

PHP MVC - Model needs to access data from another model

I have built a small PHP MVC framework and just want to clarify the best way to get data from one model into another. For example:
I have a Users_model that contains a method called get_users().
I also have Communications_model that needs to get specific or all user data and as such needs to access the get_users() method from the Users_model.
Is it best practice to:
a) Instantiate the Users_model in a controller and pass the data from the get_users() method into the Communications_model?
b) Instantiate the Users_model inside the Communications_model and run get_users() from there, so it can be accessed directly?
c) Another way?
Many thanks for any help.
It depends of your motive behind this.
If you want effect on result, then using well know library, like Doctrine etc. should be your choice.
If you want to learn design patterns, then you should get read about ActiveRecord or DataMapper + Repository patterns. Then implements both and check out.
If you want your code, this way - ORM should represent relations of data, then you should ask what it more important? If you menage communication (bus, train), then user can be there assigned and getting users from communication is OK. If user have communication (like car), then relation is reversed.
All depends, what is you motive behind this. Using library, like Doctrine, could you help you running you application. If you want learn design patterns, then check out both options to get some experience.
What you call "users model" is a repository. And what you call "communication model" looks like a service.
Your communication service should have the user repository passed in constructor as a dependency.
I honestly think, that a huge part of your confusion is that you try to call all of those things "models". Those classes are not part of the same layer. You migth find this answer to be useful.
All are possible ways but what I usually do is, whenever there is any function that I think would be reused a number of times by a number of objects, I declare it as static.
It would save the effort of playing with object declaration and would be easily accessible by ClassName::function();
Again, it's a design choice, usually objects are declared right there in the controller and used as per the need but just to save declaration of objects again and again I follow the approach of declaring function static.
The simple principle here is using the __construct() (constructor) to build the object with the relevant properties from the Database. The User Model will have a static function (therefore accessible through any scope) to create an array of instanced objects by simply passing the model data through a new self() which returns the instance.
The concept is you end up with an array of User_Model instances each being a build of the Database columns to properties. All that's left is to create the Database Model and the functions to retrieve the columns and data.
class Communications_Model {
private $_all_users;
public function getUsers() {
$this->_all_users = Users_Model::loadAllUsers();
}
}
class Users_Model {
private $_example_property;
public function __construct($user_id) {
$data = SomeDatabaseModel::getConnection()->loadUserFromDatabase((int)$user_id);
$this->_example_property = $data['example_column'];
}
public static function loadAllUsers() {
$users = array();
foreach(SomeDataModel::getConnection()->loadAllUsers() as $data) {
$users[] = new self($data['user_id']);
}
return $users;
}
}
Of course, now, you have a $_all_users; property that has an array of instanced User Models containing the data.

Initializing objects on PHP MVC - before view or during

I'm building a simple MVC Framework and stuck in how i set up the objects.
TL;DR: Is better initialize objects before the view or during the view render?
Example:
CONTROLLER
<?php
class Controller {
public function __construct() {
$user = new User();
}
}
?>
OBJECT USER
<?php
class User {
public function __construct() {
$this->setFriends($arg);
}
public function setFriends($arg) {}
public function getFriends() {}
}
?>
OBJECT FRIEND
<?php
class Friend {
.. properties ..
.. methods() ..
}
?>
VIEW
<?php
foreach($user->getFriends() as $friend){
.. $friend is a Friend Object already ..
.. html...
}
?>
The question: Is better initialize Friend object on setFriends method (before the load view - remember, there are a lot of friends) or on getFriends method (on load view)?
public function setFriends($arg) {
foreach($arg as $item)
$this->friends[] = new Friend($item)
}
OR
public function getFriends() {
$tmp = array();
foreach($this->friends as &friend)
$tmp[] = new Friend($friend)
return $tmp;
}
I think in the first case, memory will be pre consumed. And the second case, the Friend Object will initialize only if the view call getFriends.
As a general rule of thumb:
Anything that's required to make every page load should be required universally early on in the initialization (usually in a boostrap layer or similar). This includes things like a base controller (from which others extend), base view (same deal here), database handler object, etc.
Anything that's specific to just one page, as you seem to be describing with users and friends, should be loaded in the controller or controller action which handles loading that page. This helps keep your code focused and your memory footprint down.
In these cases it is always better to move as much of the business logic out of your views as you can, and save your PHP in views for simple things like loops and echos. In MVC frameworks you'll often see arrays built in a controller, so that lots of data that has already been finalized can be passed to the view. For example, within your controller you could instantiate your user to pass to the view as an argument, and then also instantiate your friends and add them all to an array of friends that you pass as another argument. Or combine these two arrays into one big array, and pass a single 'parameters' argument to the view. This would then be a standard parameter that all your views could share, and then picking apart the array of data happens within the view itself.
Other options become more viable depending on what information you need to be available about friends. For instance, when instantiating a user you could also (within the constructor) instantiate each one of their friends, assign to an array, and save them all as a property of that user. This does make a bulkier object, and you have to consider if you're using a lot of users how much this will cost.
You could, however, only need friends in certain circumstances so it may make more sense to instantiate them when you need them, instead of always having them even when you don't. If this is the case, your user should at least have a lookup of its friends, and be able to set a property within itself that will hold the info you need to look up a friend. This means that whether it's included in your user constructor (if you'll always need to know what friends a user has), or in a separate function like getFriends (if you only sometimes have to know about a user's friends), you'll need to have at least an ID of each friend that can be stored as a property of your user, so you can later loop through it and instantiate friends based on id.
Overall I think the important point is regarding context. Where you create an object directly affects two main things: where it is accessible, and how much memory you waste. You always need to weigh those two and strike the balance. My best advice is to restrict where data exists and where it is accessible as much as possible, to only those places where it has to be. This will keep you application the most secure and use the least memeory.
I know it's a lot to think about, but I hope this helpled!

In Magento what is the correct data flow from POST to controller to model to block to template?

When data is sent to a Magento extension via GET or POST, what is the correct way to use the MVC to validate the data, process it based on some business logic then output the results to the screen?
As I understand it, the controller is the correct place to receive and validate the data being submitted (perhaps calling on a model to do the actual validating). Models are the correct place for any business logic, taking the request parameters and processing them. The block is the correct place to prepare data for output, which it can request from a model.
I understand how a controller can receive posted data and forward it to a model
class Company_Project_IndexController extends Mage_Core_Controller_Front_Action
{
public function receivePostedData()
{
$model = Mage::getModel('project/somemodel');
if($model->validateData( $this->getRequest()->getPost('post_vars') )) {
$model->processData( $this->getRequest()->getPost('post_vars') );
}
}
}
which the model will then process
class Company_Project_Model_Somemodel extends Mage_Core_Model_Abstract
{
public function validate( $data )
{
//return true of false if data is valid/invalid
}
public function processData( $data )
{
//Do something with the data
}
public function getData()
{
//return something
}
}
I also understand how a block can instantiate a model and get data from it
class Company_Project_Block_Display extends Mage_Core_Block_Template
{
public function getData()
{
$model = Mage::getModel('project/somemodel');
return $model->getData();
}
}
The problem I have is the disconnect between the controller and the model working together and the Block and the model working together. If the controller is pushing data into the model which is changing the models state, how do I then get data into the block from the model based on that changed state?
As I see it, there are the following possible solutions:
Use Mage::getSingleton to get my model so I'm referring to the same instance in the controller and the block
Refer to the post data directly inside the model or the block
Use the registry to store some of the model state
I've know idea if any of the above are correct or which one I should be using.
I've taken a look at the core Catalogue Search module as its doing exactly what I need, but I got completely lost in the code.
I know that several other very similar questions to this have been asked and answered but I'm still in the dark.
It's a good question, but there isn't one clear answer. If you look at the Magento core itself, you'll see a variety of methods used. All that said, here's some general advice/context for the tradeoffs involved.
Based on usage, Magento's controller action methods are not designed to interact with The View (or as it's known in Magento, The Layout). Instead, a controller action is meant as the main entry point for a specific URL, which should then interact with the request and do stuff with models. When that's done, the controller action then tells the layout to render with a calls to
$this->loadLayout();
$this->renderLayout();
As designed (and this is just an opinion) The Layout is completely decoupled from the rest of the system. CodeIgniter/Kohana style systems feature "dumb views", which take variables from the controller action and do a simple template replacement on them. The Magento Layout, on the other hand, is a collection of nested block objects, and block object methods are meant to query the models directly when they need information.
For example — take a look at the Mage_Adminhtml_Block_Catalog_Product_Attribute_Set_Main_Formset block
#File: app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main/Formset.php
protected function _prepareForm()
{
$data = Mage::getModel('eav/entity_attribute_set')
->load($this->getRequest()->getParam('id'));
//...
}
The idea here is, irrespective of what happens in the controller action, the Mage_Adminhtml_Block_Catalog_Product_Attribute_Set_Main_Formset block will always grab the latest eav/entity_attribute_set model information when it renders (accessing the request object to get the request/post data, as you mentioned above). The controller action and the view are decoupled from one another.
As designed, the is The Right way to use the system. Unfortunately, it's also ineffective from a performance standpoint. For developers coming form other systems, the idea that view rendering kicks off another round of SQL queries seems crazy. Magento's famous "start shooting and let the caching layer" sort it out approach met with mixed results.
One remedy for this potential performance problems is where stashing already instantiated models in the registry comes into play. You can see an example of this in the admin console's product editing controller
#File: app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php
protected function _initProduct()
{
//...
Mage::register('current_product', $product);
//...
}
and then its use in many of the resulting blocks which render the editing form for products
app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Js.php
public function getProduct()
{
return Mage::registry('current_product');
}
The trade-off here is better SQL performance (no extra queries for the blocks), but you've essentially tied these blocks to a controller action which defines a current_product registry entry. The Magento registry is (in essence) a fancy way of doing global variables, so you have most of the problems inherent with globals. As you noted, you can achieve similar results with the getSingleton method — but that may not be appropriate depending on the task you're trying to accomplish.
For what it's worth, the registry/singleton method appears to be favored by the core developers for both the admin console application (the Mage_Adminhtml module) and the Magento Mobile controllers (the Mage_XmlConnect module), while the more paranoid "load the model explicitly" pattern is used more in the front-end cart application. Whether this is a coincidence, a conscious choice, or just the core team itself learning as it went along is probably one of those unanswerable questions.
Finally, one technique that's not used much by the core code, but that I sort of like when I'm looking for more of a "dumb view" behavior, is this. After you load the layout, but before you render it, you're able to access specific blocks by name. This, combined with the power of magic getters and setters, lets you do something like this
$this->loadLayout();
$content = $this->getLayout()->getBlock('content');
if($content)
{
$content->setSomeValue('Hello World');
}
$this->renderLayout();
and then from your block's template, you can grab the variable with
echo $this->getSomeValue();
You're still tying your block's implementation to your controller action but doing so in a less global-ish way. Also, the decoupled layout may not contain the block you're looking for, so you'll want to be careful with this.

PHP OOP core framework

I am just posting this question so some of you might be able to point me in the right way. I am slowly warming up to OOP, starting to understand the concept. I want to make a good solid core or foundation to be used as a CMS backend. It will also use MVC. I have been using http://gilbitron.github.com/PIP/ as the MVC- base.
The thing I cant figure out is the following:
Say, on the projectpage in the backend I have 2 sections: htmltext and projects and I should be able to edit them both. The uri would be something like:
//domain/backend/projects (the method would be the index and show the 2 sections)
When i click on projects how should it be handled?
//domain/backend/projects/projects/ or
//domain/backend/projects/list/
One step further, a project will hold some images or a gallery:
//domain/backend/projects/edit/5/gallery/2
My question here is, first: would this be a good way to go and even more important how would this be implemented in OOP
the main projects controller:
class projects {
function index(){
// view index
}
function edit{
$project = new Project();
$projectdata = $project->load(5);
}
}
A single project controller
class project {
function __construct(){
$this->projectmodel = $this->loadModel('project_model'); // prepare the model to be used
}
function load($id){
$this->projectmodel->loadproject($id);
}
}
project model
class project_model extends model { //extends for DB access and such
function __construct(){
// do stuff
}
function loadproject($id){
return $this->db->query("SELECT * FROM projects where id=" . $id . " LIMIT 1");
}
}
Now my question. If this project has images, where should I load the image class to handle those? Should I load it in the project_model like $this->images = new Images(); and have a function inside the model
function loadimages($id){
$this->images->load($id);
}
and then images would be something like:
class image extends model { //extends for DB access and such
function __construct(){
}
function load($id){
return $this->db->query("SELECT * FROM project_images where id=" . $id . " LIMIT 1");
}
}
It seems controllers and models gets mixed up this way. Yet in a logical way a project is a container that holds projectinfo, which could be text, images and maybe video's. How would I go about to set that up logically as well.
The original question
The first part, about the URLs is something called: Routing or Dispatching. There is quite good article about it in relationship with Symfony 2.x, but the the idea behind it is what matters. Also, you might looks at ways how other frameworks implement it.
As for your original URL examples, galleries will be stored in DB. Won't they? And they will have a unique ID. Which makes this, /backend/projects/edit/5/gallery/2 quite pointless. Instead your URL should look more like:
/backend/gallery/5/edit // edit gallery with ID 5
/backend/project/3 // view project with ID 3
/backend/galleries/project/4 // list galleries filtered by project with ID 4
The URL should contain only the information you really need.
This also would indicate 3 controllers:
single gallery management
single project management
dealing with lists of galleries
And the example URLs would have pattern similar to this:
/backend(/:controller(/:id|:page)(/:action(/:parameter)))
Where the /backend part is mandatory, but the controller is optional. If controller is found , then id ( or page, when you deal with lists ) and action is optional. If action is found, additional parameter is optional. This structure would let you deal with majority of your routes, if written as a regular expression.
OOP beyond classes
Before you start in on using or writing some sort of PHP framework, you should learn how to write proper object oriented code. And that does not mean "know how to write a class". It means, that you have to actually understand, what is object oriented programming, what principles it is based on, what common mistakes people make and what are the most prevalent misconceptions. Here are few lecture that might help you with it:
Inheritance, Polymorphism, & Testing
Advanced OO Patterns (slides)
Unit Testing
The Principles of Agile Design
Global State and Singletons
Don't Look For Things!
Beyond Frameworks (slide)
Agility and Quality (slides)
Clean Code I: Arguments
Clean Code III: Functions
This should give you some overview of the subject .. yeah, its a lot. But is suspect that you will prefer videos over books. Otherwise, some reading materials:
PHP Object-Oriented Solutions
Design Patterns Explained
Patterns of Enterprise Application Architecture
You will notice that a lot of materials are language-agnostic. That's because the theory, for class-based object oriented languages, is the same.
P.S.
Be careful with extends keyword in your code. It means "is a". It is OK, if class Oak extends Tree, because all oaks are trees. But if you have class User extends Database, someone might get offended. There is actually an OOP principle which talks about it: Liskov substitution principle .. also there is a very short explanation
If this project has images, where should I load the image class to handle those? Should I load it in the project_model like $this->images = new Images(); and have a function inside the model
Yes. The goal of the model is to encapsulate business logic so that you only need to write the algorithms once, and share them multiple times. What I would do if I were you is to add a getImages() method to the Project model as the images are a direct attribute to a project, this makes it so that each project knows how to retrieve it's own images.
Your code is missing a few key concepts compared to ORMS I have worked with (hydration and peer/table) classes, so I will try to stick with your code. You can retrieve the images through the Project object, then reference the images via the view 1 of 2 ways:
// Controller
$this->project = new Project();
$this->projectImages = $project->getImages(); // Implemenation #2
// View
// Implemenation #1 (reference to object initialized in controller)
<?php foreach ($project->getImages() as $image): ?>
<img src="<?php echo $image['path'] ?>" />
<?php endforeach ?>
// Implemenation #2 (reference to separate variable initialized in controller)
<?php foreach ($projectImages as $image): ?>
<img src="<?php echo $image['path'] ?>" />
<?php endforeach ?>
Above is demonstrative only, but should give you an idea. I would suggest that you take a look at Doctrine. It is a proven and stable ORM which provides much of what you are attempting to write, plus it adds the missing components that I mentioned earlier; hydration, object relations, and Table classes, along with many other features such as nested sets.
(since the question was long enough by itself I didn't want to extend it so I've added this as an answer)
projects.php
this controller extends the project controller. the main purpose is to handle the multiple getAllProjects() using its own model and then pass it on to the project controller getProject()
class Projects extends Project{
public function __construct()
{
parent::__construct();
$this->projects_model = new Projects_model();
}
public function getAllProjects()
{
$res = $this->projects_model->getAllProjects();
if($res) foreach($res as $v) $projects[] = $this->getProject($v);
return $projects;
}
}
project.php
the project_model does nothing more than getting projectdata and related data like images
class Project{
public function __construct()
{
$this->project_model = new Project_model;
$this->images = new Images();
}
public function getProject($data=NULL)
{
if($data==NULL) $data = $this->project_model->loadProject($data['id']);
$images = $this->project_model->loadProjectImages($data['id']);
return array('project_id' => $data, 'project' => $data, 'images' => $images);
}
public function getProjectByID($id)
{
$data = $this->project_model->loadProject($id);
return getProject($data);
}
}
The upside of this approach, I can use the project class on it own and it will work. I can encapsulate it in projects and it can be used as well.
Is this a good (and the right) approach to do stuff? Or would it be better to put this all together in one controller and one model class?

CodeIgniter: Where should a particular functionality go?

Here is a quick overview of the controllers functionality in most of the application:
controller loads a specific model, gets data from it, formats the data and passes the formatted data to the view.
Now there is a search page, which needs to do a search query over entire database (all models). It needs to show each type of data in its particular formatted output on a single page as a list.
The problem:
The search controller can do the search, dynamically load model for each record type, and get the data from model. Problem comes when the data needs to be formatted. I am trying to load the specific controller from the search controller, which is causing problems.
What to do?
PS: I tried using the 'Wick' library, but it fails when the controller's format function tries to use its own model and session object, giving errors about call to a member on a non-object.
After much refactoring and trial/error, It appears that the best way to achieve the above is this way:
Keep the format function in the base controller from which all other controllers are derived. The format options are passed to the function along with the data object as arguments.
Make a static function in each derived controller, which returns the formatting options of the data.
Inside the search controller (which is itself derived from the base controller), for each data object, call the static function of its particular controller which returns the data formatting options, then use that to format the object for the view.
I guess I can say I will stick to using the model only for interaction with the database, and let everything else be done by controller. If anyone has a better solution still, I am all ears.
It sounds like you want to use the Factory design pattern
Make this a library:
class MyModelFactory {
static public function Factory($data) {
$type = key($data);
return new $type($data);
}
}
now, in your controller, you can do something like this:
$model = MyModelFactory::Factory(array($_REQUEST['model'] => $_REQUEST));
and now you have an object of whatever model was specified in $_REQUEST['model']. Be sure to take any security precautions you may need for your application to assure the user has permissions to use the model that they request
Now, since you want to be using common methods and stuff, your models should probably be based off an abstract class / interface.. so instead of
class MyModelOne extends Model {
// stuff
}
You probably want something like this, to ensure your required methods will always be available:
abstract class MyAbstractModel extends Model {
protected $search_params;
public function __construct($data = array()) {
$search_params = $data['search_params'];
}
protected function GetSearchParameters() {
return $this->search_params;
}
abstract public function GetData();
abstract public function GetColumns();
abstract public function DefineViewOptions();
}
class MyModelOne extends MyAbstractModel {
public function GetData() {
$params = array();
$params[] = $this->db->escape_str($this->GetSearchParameters());
// return whatever data you want, given the search parameter(s)
}
public function GetColumns() {
// return some columns
}
public function DefineViewOptions() {
// return some configuration options
}
}
In general you can't load another controller from within a controller in CodeIgniter (although there are mods that allow you to do something like this).
I would try creating a class for formatting your data and add it to the application/library folder. Then load, use and re-use this class throughout your various controllers.
Here is a page from the CodeIgniter documentation Creating Your Own Libraries that explains the details and conventions.
Also, if a class is overkill, creating helper functions is an even lighter approach.
The difference between libraries and helpers in CodeIgniter is that libraries are classes, helpers are just a group of php functions.
Once you have formatted your data, you can load any view from any controller, so you should still have all the re-usability you need so you DRY (don't repeat yourself)
There are a few simple approaches based on the principle of what's simpler (versus what's perfectly DRY). Here's one alternative approach I use with CodeIgniter:
Instead of trying to load multiple controllers, reuse the view fragments from your search controller (or search route, depending which you're using). This requires using the same naming conventions for your data elements so the views are interchangeable, but you should be doing this anyway.
Instead of using multiple models for search, add a single Search model that knows about the things that can be searched on. If you want to prevent duplicate SQL, reuse the SQL between models (this can be done using constants, or loading SQL from disk).
Controllers are not great candidates for reuse from your own PHP code: they route actions and requests for resources to the things themselves. They are intended to be called via HTTP, using the URI interface you've come up with. Calling them from code is a coupling you want to avoid. That said, reusing controllers from JavaScript (or via cURL) is a great, decoupled way to reuse things in any web framework.

Categories