2008年12月9日星期二

Re: [fw-mvc] Another Model Design Thread

Hey there,

First off, I want to apologise to the OP for hijacking the thread and
also, apologies for making too long a post.

Thanks goes out to Matthew Weier O'Phinney and specifically to Padraic for
delving deep into it - I can truly say that I've learnt something from your
replies.

I guess I'll be refactoring my fat controllers and moving the logic into
domain models as my controllers are very, very fat and what you're saying
makes sense.

> Inheritable magic methods ;). If you use a specific mapping convention,
> you can create a __call() method among other things to proxy calls to a
> proxied layer of the Model (i.e. the data access object). Why duplicate a
> zillion methods when you can just filter incoming calls for matching proxy
> calls and divert them internally to where its appropriate?

Interesting point. This is a nice strategy and admittedly, I've note played
with magic methods as much as I'd like to.

> >We reassured ourselves by telling each other that it was because we
> wanted
> >to make the application be able to handle more than one data source and
> less
> >coupling which "stinks" there's also the old "What if we wanted to use
> >another data source? like the file system for instance?" (ahh right...
> >yes... best to stay flexible)
>
> Do be careful though - in a round about way using the multiple source
> excuse won't fly in reality. Will you really really really use a different
> data source?

Yes, definitely, I was illustrating the justification without the practical
means that our discussions went through just so that we could make ourselves
feel better about using the domain model.

> Here's the problem though - it couples you to the data access structure
> binding
> each Model to a specific table. That's why Zend_Db will never fully
> replace a true ORM.
> I get the feeling your main difficulty was in ORM can do on the fly,
> mapping.
> Zend_Db (indirectly) presumes a 1:1 relationship between Models and
> database
> tables which limits the directions your Model can take, which hamstrings
> them
> when applying Model design from the outside-in (starting with Model API
> and
> working back to the database).
> Controllers map user intent to Models. In a weird way, only Models alter
> Models :).
> Controllers are ignorant, stupid, lowly translators of intent into a form
> the
> Model can use (typically just other Model calls!) .For example, a user
> might
> submit an Order with a payload of data. The Controller picks up that it's
> a newly
> submitted Order, locates the Order Model, and tells it to do it's thing
> for a
> new Order with the following data. What the Model does after that is a
> mystery
> to the Controller - it's completed its function by translating the HTTP
> request
> into a Model action.
>
> That was the main message of my article - we're over reliant on
> Controllers and
> leaving behind stripped down Models. In my method, I can run a battery of
> new
> Order tests on the Model directly; under the other alternative I'd have to
> test
> via the Controller which is...not exactly pretty. Ever tried to apply TDD
> to a
> Controller? It's not worth the pain until you get as far as needing
> acceptance
> tests when your View is more finalised.
> It could use a more efficient proxy, but in the right Model it makes
> sense.
> Who says load() maps to one table only? It could be aggregating from a
> dozen.
> Or two. Either way the matching of proxy methods is a simple exercise in
> isolating
> the data access which makes adding additional tasks to those methods far
> easier
> than being forced to extend/adapt the existing Zend_Db methods. It looks
> silly
> in a really simple Model matching just one table - that's not always the
> case.
>
> >What I'm seeing here is that you're using the Zend TDG as a data source
> for
> >your data source. but TDG already has a data source - (the table)
>
> It's using TDG as a general access API to the datasource - you could use
> pure ext/mysqli
> (nothing wrong with that either). TDG automates a lot of the CRUD however
> instead
> of building it from scratch. Models generally don't give a toss where the
> data
> comes from and how - so long as it has access to something else which
> does.

Beautifully put. you've sold me on this idea of reverting back to the domain
model.

> >> I generally prefer to avoid the inherited-from route. It's appropriate
> if
> >> all you're doing is data > access (not the real use of a Model) but it
> >> tightens the coupling between your Models and the
> >> underlying framework. Ideally Models are unaware of the framework, the
> >> acid test being able > to quickly port them from one framework to
> another
> >> with little to no change.
> >
> >But say that you've developed the model specifically for use with the
> >framework and had simple requirements such as "getList()" - will it still
> be
> >wrong then?
> >
> >I've always seen the domain model as something to develop toward as your
> >requirements increase instead of using it as a starting point.
>
> It's not a universal truth - people have developed from both directions
> with great
> success. If you want a real mind bender look for an article by Martin
> Fowler
> written about the practice of using Mock Objects. He identifies two
> popular
> approaches which equate to outside-in or inside-out. Anybody who preaches
> one,
> and doesn't acknowledge the other, is failing to be a good teacher.

I consider both practical depending on whether or not you know the problem
domain well enough to make a judgement call.
Developing something from scratch with requirements still needing some
exploration, I'd usually start small and go big.

> >You're delving into writing code for others you do not trust - either
> that,
> >or creating extra loops for yourself to dive into.
> >
> >This is natural if you're just learning OOP and have read all the books.
> >
> >I myself have been through: GOF, Refactoring, POEAA, Head First design
> >patterns and when you first read it you're full of ideas. Afterward
> though,
> >you realise that half the stuff do not relate to PHP.
>
> Every tiny portion of those books absolutely relate to PHP and every other
> programming language in the Universe. Even the non-OOP ones! They form the
> basis
> of a universal knowledge you can apply everywhere. If you have that, any
> other
> programming language is reduced to mere syntactical differences and use
> cases.
>
> I really encourage you to resist that mindset.

I don't agree with this. I'm talking about the design patterns and
principles that are directly related to strongly typed languages. Not so
much the general principles and design patterns - I mean, patterns that were
developed to work around persistent datasources such and languages without
state. I've seen a couple of books which try to shoe horn such patterns to
PHP and I couldn't see how or why it would be used.

> Hope the replies made sense. :) Don't take it as raining on your parade

Not at all, I was not trumpet blowing or anything like that, my previous
post was actually a genuine attempt in getting to know why you guys are
using domain model because we had been using the domain model pattern and
shied away from it for reasons mentioned. Seeing as so many of you actually
see it as the semantic way of doing things, I wanted to know why, because in
our attempt to use the pattern, it f*cked things up - but admittedly, I
guess this was due to our inexperience.

Thanks again for responses and also to Matthew Ratzloff as well for
contributing and highlighting that validation should indeed be done in the
model.

> That's completely backwards. All validation and business logic should
> happen in the model.
> The controller should be (relatively) dumb. If you have "dumb" models,
> then of course a decoupled
> model is going to seem extraneous.
> Most models have more complex business logic with methods beyond basic
> CRUD.

So in this discussion, we've established a certain approach that the Model
Layer is should be fat and not the controller.

Let's continue and dig deeper.

Please keep responses coming.

###Questions regarding forms and other components and how they work with the
model###

I had a question regarding how you handle forms and form validation and
where it all fits in.
> >Which again works as a proxy... which in this case may not be that bad...
> >don't know not going to delve too deep into that one.
> >
> >RE Views should be able to fetch their own data - I TOTALLY agree with
> you -
> >In the old framework we were using that we had created, the view had
> access
> >to a local model locator which the controller is also free to use.
> >
> >Model locator knew how to create models and was the gateway to accessing
> >models. What about forms? currently forms are fed into the view from the
> >controller. Will views be able to fetch their own forms also? if so, does
> it
> >work the same way as the model? being used by the view and the
> controller?
>
> The way I've done it previously is separating the form generation from the
> Controller,
> encasing it in a View Helper, and feeding it the Model to generate forms.
> You can even
> create a generic class for creating simple forms from the Model with
> little involvement.
> The tricky part (dodgy!) is that Zend_Form couples a few things closely so
> you also need
> to reference the form object from within the Model but you view it as a
> validation process
> (and part of the Model) it's not so bad. The main block is when you
> consider it as adding
> forms to Models (not the precise truth despite being technically correct -
> we're adding the form merely to access the validation rules).

But after going back and looking over other responses, I'll be taking the
advice that Matthew Weier O'Phinney recommended. Hey Matthew - "WIN!" indeed
:)

> Zend_Form's design is such that it can act as a value object, a
> validation chain, or as a view representation. As such, I've been
> attaching forms to my model classes. This allows me two things:

> * Ability to keep all validation logic self-contained in the model,
> leaving me with fat models and skinny controllers.
> * Ability to provide one or more form representations of my model.

> How does the first work? Well, in my controller, I now am checking if
> operations in my model succeed, rather than validating a form, injecting
> data into the model, checking to see if an operation succeeds, etc. It
> makes the logic much simpler:

> if (!$id = $this->model->insertSuchAndSuch($request->getPost())) {
> $this->view->model = $this->model;
> return;
> }
> $this->_redirect($this->_helper->url(array(
> 'action' => 'view', 'id' => $id
> ));

> As for the second, consider the related view script:

> $form = $this->model->getSuchAndSuchForm();
> // ...
> // maybe manipulate my decorators, as the view is the right place to
> // do that
> // ...
> echo $form;

I'd imagine that the same can be done for datagrids also?

So a view script might look something like this:

<?php $model = $this->getModel('Users'); ?>

<!-- search form -->
<div id="searchForm">
<?php echo $model->getForm(); ?>
</div>
<!-- /search form -->

<!-- data grid -->
<div id="datagrid">
<?php echo $model->getDataGrid(); ?>
</div>
<!-- /data grid -->

Anyway, I'm in an analytic mood, so I'll continue.

###I'll be refactoring to this: The path back to the Domain Model and
Independent Views###

Now that I'm back to my old way of thinking, I'll be refactoring to the
following:

Components that are in the background:
- "Model Locator" gets assigned to Zend_Registry in bootstrap.
- "GetModel" Action Helper accesses the registry to get to the Model Locator
which returns a model.
- "GetModel" View Helper accesses the registry to get to the Model Locator
which returns a model.

Flow of operation

- Controller selects the view based on request. // this is already done for
you by ZF.
- Controller selects the model (through an action helper) and delegates
operations such as validation and CRUD based on the request.
- The Domain Model validates input and uses TDG (Zend Table) as an API to
the datasource to manipulate data.
- The View uses a view helper to acquire the Model and use it's public
methods to get the data it needs without being fed data from the controller.
It pulls data from the model and it knows what it needs.

A controller action might look something like this:

public function indexAction()
{
if ($this->getRequest()->getPost()) {
$model = $this->_helper->getModel('Users');
if($model->save($this->getRequest()->getPost())) {
$this->_helper->FlashMessenger('Record Saved');
$this->_redirect($this->_helper->url(array('module'=>'users',
'controllers'=>'index',
'action'=>'index')));
} else {
$this->_helper->FlashMessenger('Errors in form');
$this->_redirect($this->_helper->url(array('module'=>'users',
'controllers'=>'index',
'action'=>'edit',
'id'=>$model->getId())));
}
}
}

The view script for index action might look something like this:

// view script for users/scripts/index.phtml
<?php
$model = $this->getModel('Users'); // the controller no longer needs to
select the model for the view as it can do it itself.
$route = array('module'=>'users', 'controller'=>'index', 'action'=>'edit',
'id'=>$model->id);
?>

<!-- user details -->
<div id="userDetails">
<p>Hello <?php echo $model->firstname; // or are we doing
$model->getUserName(); since we'll have magic __call() function in our base
domain model class in place of getters/setters? ?></p>
<p>Your registered details are of the following:</p>
<ul>
<li> "<?php echo $this- url($route,null,true); ?>"><?php echo
$model->getFullName(); // model concatenates title, firstname, middlename
and lastname in a defined accessor method; ?> </li>
<li> "<?php echo $this- url($route,null,true); ?>"><?php echo
$model->getEmail(); ?> </li>
<li> "<?php echo $this- url($route,null,true); ?>"><?php echo
$model->getPhone(); ?> </li>
<li> "<?php echo $this- url($route,null,true); ?>"><?php echo
$model->getAddress(); ?> </li>
</ul>
</div>
<!-- /user details -->

<!-- user scripts -->
<div id="userScripts">
<?php echo $model->getDataGrid(); // access to datagrids is kinda funny,
it's like form... it has it's own little thing. It's rendering data, so it
might have a place in the view, it also has it's own data source and search
form which makes it a little strange. This is something I need to help
resolving. ?>
</div>
<!-- user scripts -->

Can you guys see anything wrong with the above?

What about resources like datagrids etc?

regards,


-----

R. Villar David
Lead Developer, DevProducts Pty Ltd
p: +61 2 9648 3777 f: +61 2 9648 6988
w: http://www.devproducts.com
--
View this message in context: http://www.nabble.com/Another-Model-Design-Thread-tp20909700p20928316.html
Sent from the Zend MVC mailing list archive at Nabble.com.

没有评论: