SitePoint Sponsor |
|
User Tag List
Results 51 to 68 of 68
Thread: My First crack at MVC
-
Oct 24, 2006, 08:08 #51
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
PHP Code:class ProductItemController extends ProductController {
...
$this->view=& new ProductItemView($this->model,$getvars['id']);
...
I think the case may be that it's misleading to talk about proper MVC and improper MVC. There are different degrees of separation, and more separation isn't necessarily better.
-
Oct 24, 2006, 09:50 #52
Originally Posted by rockit
-
Oct 24, 2006, 12:42 #53
- Join Date
- Apr 2006
- Location
- Pennsylvania
- Posts
- 1,736
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
rockit,
I don't think the idea is to get the "rules" of MVC perfectly correct according to someone's idea of MVC. The idea is that the pattern is supposed to allow your application to be more flexible, scalable, etc, and that the layers are separated. Like I said in my last post, whatever works for you is what you should use. If you at least have the basic idea of what generally is best practice, then you go from there and add in your own experience, and form it to fit into your application.
Do I care that my implementation might not be perfect according to some people's idea of MVC? Not really. All I care about is that it is working very well for me right now, and in the very good chance I will run into limitations, I will refactor. That's what I'm in the process of doing right now, in fact. My navigation was hardcoded, but now I'm finding myself copying navigation elements to point to certain pages from one class to another, so I'm moving the list of navigation into the database so it can be more dynamic. I've run into other issues along the way, also, and I've had to refactor to resolve those.
Nothing more paralyzing than overanalysis.
-
Oct 27, 2006, 08:01 #54PHP Code:
class CategoryView {
public $model;
public $tpl;
public function __construct() {
$model = new CategoryModel();
$tpl = new Template('views/tpl/');
}
}
class AddCategoryView extends CategoryView {
public $model;
public function __construct() {
$model = new CategoryModel();
}
public function execute() {
$tpl = new Template('views/tpl/');
echo $tpl->fetch('tpl.AddCategory.php');
}
}
PHP Code:class CategoryController {
public $view;
function __construct() {
}
}
class AddCategoryController extends CategoryController {
public function __construct() {
if (isset($_POST['submit'])) {
$view = new CategoryAddedView();
$view->execute();
$view->model->addCategory();
} else {
$view = new AddCategoryView();
$view->execute();
}
}
}
PHP Code:<?php
class CategoryModel {
public $sql;
public $db;
public function __construct() {
echo 'Model is now talking';
}
public function addCategory() {
//$sql = "INSERT INTO category category_name='', category_keywords='', category_description='', category_dateadded='', category_sort_order='', category_image=''";
echo 'addCategory Method Called';
// $db::execute($sql);
}
public function editCategory() {
$sql = "UPDATE category SET category_name='', category_keywords='', category_description='', category_dateadded='', category_sort_order='', category_image=''";
// $db::execute($sql);
}
public function viewCategory() {
$sql = "SELECT * FROM category WHERE category_id=''";
// $db::execute($sql);
}
public function deleteCategory() {
$sql = "DELETE FROM category WHERE category_id=''";
// $db::execute($sql);
}
public function listCategory() {
$sql = "SELECT * FROM category";
// $db::execute($sql);
}
}
?>PHP Code:<?php
include 'models/CategoryModel.php';
include 'models/ProductModel.php';
include 'controllers/CategoryController.php';
include 'views/CategoryView.php';
include 'lib/Template.php';
$map = Array(
'add' => 'AddCategoryController',
'edit' => 'EditCategoryController',
'delete' => 'DeleteCategoryController',
'view' => 'ViewCategoryController',
'' => 'ListCategoryController',
);
$name = $map[$_GET['page']];
$controller = new $name($_GET);
?>
keep getting Fatal error: Call to a member function addCategory() on a non-object in C:\Inetpub\wwwroot\store-front\admin\controllers\CategoryController.php on line 26 when i'm trying to call $view->model->addCategory();.Last edited by rockit; Oct 28, 2006 at 14:16.
-
Nov 25, 2006, 01:59 #55
- Join Date
- Jun 2004
- Location
- Netherlands
- Posts
- 172
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
Kyberfabrikken (or someone else), could you explain how the by the book example is supposed to work? The model works fine, it's the MyView which gives problems:
PHP Code:<?php
class MyModel
{
protected $name = "World";
static protected $instance;
static function getInstance() {
if (!isset(self::$instance)) {
self::$instance = new MyModel();
}
return self::$instance;
}
function getName() {
return $this->name;
}
function setName($name) {
$this->name = $name;
}
}
class MyView
{
function execute() {
$m = MyModel::getInstance();
echo "Hello ".$m->getName();
}
}
if ($_SERVER['HTTP_METHOD'] == "POST") {
$m = MyModel::getInstance();
$m->setName("PostWorld");
}
$view = new MyView();
$view->execute();
?>
Even if I leave out the SERVER[''] stuff, the view won't return anything. If anyone can explain what's supposed to happen here, thanks!
-
Nov 25, 2006, 07:47 #56
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
HTTP_METHOD should be REQUEST_METHOD
The model will change if the request-method is POST (Eg. if the page is loaded as a result of a form submit). Since there is no form in the view, this can't really be tested. Try this:
PHP Code:<?php
class MyModel
{
protected $name = "World";
static protected $instance;
static function getInstance() {
if (!isset(self::$instance)) {
self::$instance = new MyModel();
}
return self::$instance;
}
function getName() {
return $this->name;
}
function setName($name) {
$this->name = $name;
}
}
class MyView
{
function execute() {
$m = MyModel::getInstance();
echo "Hello ".$m->getName() . "<br>" . "<form method='post' action=''><input type='submit' value='postit' /></form>" . "<input type='button' onclick=\"location.href='".$_SERVER['PHP_SELF']."'\" value='getit' />";
}
}
if ($_SERVER['REQUEST_METHOD'] == "POST") {
$m = MyModel::getInstance();
$m->setName("PostWorld");
}
$view = new MyView();
$view->execute();
?>
-
Nov 26, 2006, 07:01 #57
- Join Date
- Jun 2004
- Location
- Netherlands
- Posts
- 172
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
Thanks, that did it. I was focusing so much on the oop aspect that I didn't see that.
-
Nov 27, 2006, 03:39 #58
- Join Date
- Nov 2006
- Location
- Australia
- Posts
- 233
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
After reading this, it's apparent to me that the relationship between a model and view could potentially be one to many.
If so, then the MV part of the MVC triad could easily be coded using the observer pattern. The model is the subject and the view(s) could then be the observers.
Or maybe I've just got patternitis.
Consider the following excerpt.
Let's call this block the controller logic. Like say in an action/command or something.
PHP Code:$frontView = new FrontView($model = new BodyWeight()); // the view registers itself in constructor for each instance.
$sideView = new SideView($model);
$backView = new BackView($model);
$frontView->display();
$sideView->display();
$backView->display();
$model->gainWeight(10); // notify() method invoked which updates all three views.
#should now display aspects front, side and back looking 10 kilos heavier.
$frontView->display();
$sideView->display();
$backView->display();
$model->unregister($frontView); //removes frontView View.
$model->weightGained(20); // again runs notify() method. This time only notifying two remaining registered views.
$frontView->display(); #displays 10 kilo weight gain aspect (it was unregistered before 20 kilos were gained)
$sideView->display(); #displays 30 kilo weight gain aspect
$backView->display(); #displays 30 kilo weight gain aspect
Would the relationship between the model and the view(s) be considered as "pure mvc"?
RV David
Web Development Blog
-
Nov 27, 2006, 05:19 #59
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by rvdavid
Originally Posted by rvdavid
-
Nov 27, 2006, 06:29 #60
- Join Date
- Nov 2006
- Location
- Australia
- Posts
- 233
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by kyberfabrikken
Many models can have many Views.
To continue on from previous example
PHP Code:$frontView = new FrontView($hairModel=new HairStyle(), $weightModel=newBodyWeight());
$sideView = new SideView($model,$weightModel);
$backView = new BackView($model,$weightModel);
controller logic.
PHP Code:/*if you get a rise out of using multiple patterns (like I do), maybe you could use a Registry for the model. So you could dump in as many data models as you want.
This is just going off the top of my head however, so don't take it for bible, I just saw the need to pass an untold amount of parameters/objects, so I thought registry. If you want to simplify it, you could even just use an array*/
#create the registry of models
$registry = new ModelRegistry();
$registry->add($hair=new HairStyle());
$registry->add($weight=new BodyWeight());
#instantiate views;
$frontView = new FrontView($registry);
$sideView = new SideView($registry);
$backView = new BackView($registry);
$hair->cut('mullet'); // calls notify() after change
$weight->gain(15); // calls notify() after change
$frontView->display(); // displays frontal mullet and weight gain of 15 kilos
$sideView->display(); // shows sideview of mullet and weight gain of 15 kilos
$backView->display(); // shows rear view of mullet and weight gain of 15 kilos
$frontView->unregister(); // sorry bad example last time, subscribers are usually the ones who are subscribing and unsubscribing
$hair->cut('crewcut'); // won't update frontView because it's unregistered itself.
$frontView->display(); // displays frontal mullet and weight gain of 15 kilos
$sideView->display(); // shows sideview of crewcut and weight gain of 15 kilos
$backView->display(); // shows rear view of crewcut and weight gain of 15 kilos
Off Topic:
sorry for hijacking the thread, but I'm trying to contribute as well as reaffirm some things I've come accross in my travelsRV David
Web Development Blog
-
Nov 27, 2006, 06:31 #61
- Join Date
- Nov 2006
- Location
- Australia
- Posts
- 233
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Putting aside the fact that this code I've called "controller logic" needs some refactoring badly, this is still valid right?RV David
Web Development Blog
-
Nov 27, 2006, 07:01 #62
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by rdavid
Either way, a registry is a good idea for managing shared objects. That would very often be model layer components. I suggest using a local registry, so this rules out static classes, singletons and other variations of globals. While you're at it, you may as well combine it with a factory. This gives two benefits:
- Lazy load.
You don't create objects that you don't need. - Abstracting away dependencies.
Since the constructor is encapsulated in the factory, client code doesn't need to know which dependencies a given class has.
Originally Posted by rdavid
Originally Posted by rdavid
-
Nov 28, 2006, 16:23 #63
- Join Date
- Nov 2006
- Location
- Australia
- Posts
- 233
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by kyberfabrikken
What I meant was "traditional" MVC.
Originally Posted by kyber
But now I'm playing around with the following ideas:
- Use a registry for models and pass it to Controller
- Controller creates view
- Controller modifies model
- View is updated when model changes.
Which is, to my understanding, a traditional MVC implementation.
PHP Code:<?php
/*bootstrap*/
$modelRegistry = new ModelRegistry();
// ignore dispatch systems etc at this stage. Lets just pretend that the below code is what's been created by front controller.
$appController = new SomeAppController($modelRegistry);
$appController->run();
/* App Controller */
class AppController
{
public function __construct(Registry $modelRegistry)
{
$this->_models = $modelRegistry;
}
public function indexAction()
{
$view = new FrontView($this->_models);
$view->display();
$this->_models->getWeightModel()->gainWeight(10);
$view->display(); // should display 10kgs heavier.
}
public function run()
{
//dispatch to action code;
}
}
/* Model */
class WeightModel
{
protected $_connection;
protected $_views;
protected $_weight;
public function __construct($connection)
{
$this->_connection = $connection;
// somehow get weight.
}
public function notify()
{
foreach ($this->_views as $view) {
$view->update();
}
}
public function gainWeight($weight)
{
$this->_weight += $weight;
}
public function getWeight()
{
return $this->_weight;
}
public function addView($view)
{
$this->_view[get_class($view)] = $view;
}
public function removeView($view)
{
$class = get_class($view);
if (isset($this->_view[$class])) {
$this->_view[$class];
}
}
}
/* View */
class FrontView
{
protected $_weight;
protected $_height;
protected $_image = 'questionmark.jpg';
public function __construct($registry)
{
$this->_weight = $registry->getWeightModel();
$this->_height = $registry->getHeightModel();
}
public function update()
{
$weight = $this->_weight->getWeight();
$height = $this->_height->getHeight();
switch($weight)
{
case $weight > 80:
// do some calculation with height to figure out what pic to use for front view.
$this->_image = 'frontMedium.jpg';
break;
case $weight < 80:
$this->_image = 'frontSmall.jpg';
break;
default:
$this->_image = 'frontNone.jpg';
break;
}
}
public function display()
{
echo '<img src="/images/'.$this->_image.'" />';
}
}
?>RV David
Web Development Blog
-
Nov 29, 2006, 03:05 #64
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Your model could easily broadcast more particular events, such as onWeightChange or even onWeightGain. If you have worked with javascript/DOM, you will recognize that the DOM is following this pattern. In javascript, you can subscribe to events that happen in the DOM (Document Object Model).
A couple of weeks ago, I was tinkering with a small experiment. The code for subscribe/notify is something that you need in a lot of places, so I have had trouble finding out where to put it. Currently, my model classes will either share a common base class, or I'll have to reimplement the same few lines of code in multiple places - not exactly the best solution.
So inspired by javascript's event model, I stitched the following together:
PHP Code:Signals::$instance = new Signals();
function connect($subject, $event, $objOrFnc, $methodOrNothing = NULL) {
return Signals::$instance->connect($subject, $event, $objOrFnc, $methodOrNothing);
}
function signal($subject, $event) {
$args = func_get_args();
array_shift($args);
array_shift($args);
return Signals::$instance->signalArgs($subject, $event, $args);
}
function signalArgs($subject, $event, $args) {
return Signals::$instance->signalArgs($subject, $event, $args);
}
function signalAll($subject) {
return Signals::$instance->signalAll($subject);
}
function disconnect($subject, $event, $objOrFnc, $methodOrNothing = NULL) {
return Signals::$instance->disconnect($subject, $event, $objOrFnc, $methodOrNothing);
}
function disconnectAll($subject, $event) {
return Signals::$instance->disconnectAll($subject, $event);
}
function hasConnected($subject, $event) {
return Signals::$instance->hasConnected($subject, $event);
}
class Signals_BreakSignalException extends Exception {}
/**
* Class is a singleton. You probably don't want to instantiate it, but
* rather use the globally scoped functions.
*/
class Signals
{
static $instance;
protected $subjects = Array();
protected $delegates = Array();
protected function delegate($objOrFnc, $methodOrNothing = NULL) {
foreach ($this->delegates as $key => $delegate) {
if ($delegate[0] === $objOrFnc && $delegate[1] === $methodOrNothing) {
return $key;
}
}
$this->delegates[] = Array($objOrFnc, $methodOrNothing);
return count($this->delegates) - 1;
}
public function connect($subject, $event, $objOrFnc, $methodOrNothing = NULL) {
if (!is_object($subject)) {
throw new Exception("Wrong argument type. Subject must be an object.");
}
$d = $this->delegate($subject);
if (!isset($this->subjects[$d])) {
$this->subjects[$d] = Array();
}
if (!isset($this->subjects[$d][$event])) {
$this->subjects[$d][$event] = Array();
}
$this->subjects[$d][$event][] = $this->delegate($objOrFnc, $methodOrNothing);
}
public function disconnect($subject, $event, $objOrFnc, $methodOrNothing = NULL) {
if (!is_object($subject)) {
throw new Exception("Wrong argument type. Subject must be an object.");
}
$d = $this->delegate($subject);
if (!isset($this->subjects[$d])) {
return;
}
if (!isset($this->subjects[$d][$event])) {
return;
}
$d2 = $this->delegate($objOrFnc, $methodOrNothing);
$tmp = Array();
foreach ($this->subjects[$d][$event] as $id) {
if ($id != $d2) {
$tmp[] = $id;
}
}
$this->subjects[$d][$event] = $tmp;
}
public function disconnectAll($subject, $event) {
if (!is_object($subject)) {
throw new Exception("Wrong argument type. Subject must be an object.");
}
$d = $this->delegate($subject);
if (!isset($this->subjects[$d])) {
return;
}
if (!isset($this->subjects[$d][$event])) {
return;
}
$this->subjects[$d][$event] = Array();
}
public function signal($subject, $event) {
if (!is_object($subject)) {
throw new Exception("Wrong argument type. Subject must be an object.");
}
$args = func_get_args();
array_shift($args);
array_shift($args);
return $this->signalArgs($subject, $event, $args);
}
public function signalArgs($subject, $event, $args) {
if (!is_object($subject)) {
throw new Exception("Wrong argument type. Subject must be an object.");
}
$d = $this->delegate($subject);
if (isset($this->subjects[$d]) && isset($this->subjects[$d][$event])) {
foreach ($this->subjects[$d][$event] as $id) {
$delegate = $this->delegates[$id];
if (is_null($delegate[1])) {
$callback = $delegate[0];
} else {
$callback = $delegate;
}
try {
call_user_func_array($callback, $args);
} catch (Signals_BreakSignalException $ex) {
return FALSE;
}
}
}
return TRUE;
}
public function signalAll($subject) {
if (!is_object($subject)) {
throw new Exception("Wrong argument type. Subject must be an object.");
}
$d = $this->delegate($subject);
if (isset($this->subjects[$d])) {
$args = func_get_args();
array_shift($args);
foreach (array_keys($this->subjects[$d]) as $event) {
if (!$this->signalArgs($subject, $event, $args)) {
return FALSE;
}
}
}
return TRUE;
}
public function hasConnected($subject, $event) {
if (!is_object($subject)) {
throw new Exception("Wrong argument type. Subject must be an object.");
}
$d = $this->delegate($subject);
return isset($this->subjects[$d]) && count($this->subjects[$d]) > 0;
}
}
PHP Code:require_once 'signals.php';
echo "<pre>";
function myHandler() {
echo "myHandler called\n";
}
function myHandler2() {
echo "myHandler2 called\n";
}
$foo = new StdClass();
ob_start();
connect($foo, 'bar', 'myHandler');
signal($foo, 'bar');
if (assert(ob_get_clean() == "myHandler called\n")) {
echo "pass\n";
}
ob_start();
disconnect($foo, 'bar', 'myHandler');
signal($foo, 'bar');
if (assert(ob_get_clean() == "")) {
echo "pass\n";
}
ob_start();
Signals::$instance = new Signals();
connect($foo, 'bar', 'myHandler');
disconnectAll($foo, 'bar');
signal($foo, 'bar');
if (assert(ob_get_clean() == "")) {
echo "pass\n";
}
ob_start();
Signals::$instance = new Signals();
connect($foo, 'bar', 'myHandler');
connect($foo, 'bar', 'myHandler2');
signal($foo, 'bar');
if (assert(ob_get_clean() == "myHandler called\n"."myHandler2 called\n")) {
echo "pass\n";
}
ob_start();
Signals::$instance = new Signals();
connect($foo, 'bar', 'myHandler');
connect($foo, 'jazz', 'myHandler2');
signal($foo, 'bar');
if (assert(ob_get_clean() == "myHandler called\n")) {
echo "pass\n";
}
ob_start();
Signals::$instance = new Signals();
connect($foo, 'bar', 'myHandler');
connect($foo, 'bar', 'myHandler2');
disconnect($foo, 'bar', 'myHandler');
signal($foo, 'bar');
if (assert(ob_get_clean() == "myHandler2 called\n")) {
echo "pass\n";
}
$arr = Array(1,2,3,4);
$ex = NULL;
try {
connect($arr, 'bar', 'myHandler');
} catch (Exception $ex2) {
$ex = $ex2;
}
if (assert($ex2 instanceOf Exception && $ex2->getMessage() == "Wrong argument type. Subject must be an object.")) {
echo "pass\n";
}
I'm not sure how relevant this is to you, but in case you're going on with the event/notify strategy, it might help. I should note that I didn't try this in a real application yet, so use at your own risk. In particular, this would prevent any kind of garbage collection by the PHP runtime. But since that's totally broken in the first place, it may not make much of a difference.
-
Dec 4, 2006, 06:38 #65
- Join Date
- May 2005
- Location
- Holland!
- Posts
- 852
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by rockit
http://www.oreilly.com/catalog/hfdesignpat/
peace...Business as usual is off the menu folks, ...
-
Dec 4, 2006, 19:44 #66
- Join Date
- Sep 2006
- Posts
- 368
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
here maybe this can help
its quite clear and simple
http://www.phpit.net/article/simple-mvc-php5/
-
Dec 4, 2006, 21:19 #67
- Join Date
- May 2003
- Location
- virginia
- Posts
- 988
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Originally Posted by kyberfabrikken
-
Dec 5, 2006, 13:56 #68
- Join Date
- May 2003
- Location
- virginia
- Posts
- 988
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Bookmarks