I just wanted to get some more information on some things you've been saying
and get a feel from the angle you are coming from.
See I've covered and developed this type of model architecture (domain
model) on a framework we had developed in house and it just seemed too much
work, somewhat bloated and at the end it was confusing.
We had a domain model, which through the use of a TableDataGateway locator,
acquired instances of Table Data Gateways (some models needed more than one
TDGs and different models would be required in a process).
We saw a lot of:
public function save($data) {
$this->_table->save($data);
}
public function add($data) {
$this->_table->insert($data);
}
public function delete($data) {
$this->_table->delete($data);
}
After a while it makes you sick and think, well, why didn't we just make the
Table Data Gateway itself the model instead of being a proxy to the Table
Data Gateway?
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)
So off we went into our merry way with our "proxy" domain model.
As Zend came about and matured, we decided to adopt it and tried out the
extend Zend_Db_Table_ABSTRACT class - this time, we tried just using the
Table Data Gateway
as the model itself.
At this point in time, we really can't see any downsides. Less code, less
indirection. Room to refactor into bigger and better model layers... We feel
it's a win/win so far.
What I want to know is why you have decided to move away from this type of
model and shift to how we were doing it?
My team and I are pretty much pragmatic and are always open to reasonable
best practice, so it would be good to get your angle on it.
> I am not so keen on this method anymore. This is how I have done it up
> until now, but what you are returning from getList() are instances of
> Zend_Db_Table_Row_Abstract. Which means that other parts of the code
> could theoretically do something like:-
> $user_one->id = 0;
> $user_one->save();
Say that we make it so that the records produced by "getList()" is a read
only array?
Say that we decided to use setFetchModel(Zend_Db::FETCH_ASSOC); to produce
more generic array output?
Would it still be invalid?
> If we assume that "id" is an auto_increment primary key, what business
> does any other part of the application have changing that id?
Models work with Controllers... so... controllers have the right to morph
model, right?
Models are read only to views and controllers can change the model as
required.
> Instead what I am working on at the moment, is based around the idea
> that my models will "use" Zend_Db(_Table/_Row) not extend them. This
> means that the database access is locked away from the rest of the
> program, so if you are working with a team of developers, there is
> much less chance of someone making a mistake like the one above.
I know what you're saying, but is it really a mistake? I don't understand.
Controller uses the model and there are strict rules in dev teams for views
not to change the model. For instance, the team I lead know better than to
have
model morphing functionality where they do not belong.
> It also means that your models should match your application closer,
> and expose less public code. This makes your application more
> decoupled and should make it easier to maintain.
It could also bloat your model and over complicate things.
> I came up with something like:-
> class User {
> protected _table;
> protected _id;
> protected _name;
>
> public function getId(){ return $this->_id; }
>
> public function getName(){ return $this->_name; }
> public function setName( $value ){ $this->_name = $value; }
>
> public function setTableHandler( Zend_Db_Table_Abstract $table )
> { $this->_table = $table; }
> public function __construct( ){ }
>
> public function save(){
> // Use $this->_table to save the row.
> }
>
> public function load(){
> // Use $this->_table to load the row.
> }
>}
> At first, this may seem like you would be needlessly creating a lot of
> get and set methods, but a good IDE will be able to make snippets for
> you to reduce the typing, and if you are doing something like test
> driven development, then is only a small increase in the grand scheme
> of things.
Uhk! see this is why we decided to steer away from this type of model.
There's too much sh!t! How is this better than using Zend_Table data gateway
as the model?
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)
As for relying on a good IDE, code templates and phing can only get you so
far.
> Also, whose job is it to make sure your data values are in correct
> ranges? For arguments sake, lets say we are modelling a car. We might
> have something like:-
> class Car {
> protected $_num_gears;
> protected $_current_gear;
>
> public function __construct( $gears ){
> $this->_num_gears = $gears;
> $this->_gear = 0; // neutral (-1 could be reverse! )
> }
>
> public function ChangeUpGear(){
> $this->_current_gear++;
> }
> public function ChangeDownGear(){
> $this->_current_gear--;
> }
> public function SelectGear( $gear ){
> $this->_current_gear = $gear;
> }
> }
> Now it's pretty obvious where you would add extra code to make sure
>that your gear selections are within the allowed ranges. For example,
>if I tried to SelectGear(6) in a 5 gear car, I could throw an
>exception. Then if any other parts of my code attempted to do it, I
>would know right away - if I were using the models return from
> Zend_Db_Table->fetchAll(), I would not!
I see this and I think that it's obvious that the validation logic would
fall upon the controller, no?
To me I see it as models being dumb and returning data from the data source.
Controller layer handle requests, which includes validation.
> $user->loadFromArray( array('id' => 0) );
> I am thinking I am going to move towards putting the loadFromArray()
> functionality so that it is protected and you have to pass the array
> of data to the constructor. This would make it much harder to
> accidentally update the wrong value in the model.
This may be over engineering things. By thinking in the way of "This would
make it much harder to
accidentally update the wrong value in the model", you're going into
something which the framework developers are there to worry about.
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.
> Pauls approach is what I would consider good practice when it comes to
> models, you are breaking OO inheritance by extending your models from
> Zend_Db_Table. Models should have a has-a not is-a relationship with
> models.
It is, if you put it the way you have put it and you extend concrete
classes,
but what if you're extending Zend_Db_Table_Abstract which is an abstract
class, it's not "breaking OO inheritance"
also... what does that mean?
> I recently complained (i.e. ranted) on the topic over at
> http://blog.astrumfutura.com/archives/373-The-M-in-MVC-Why-Models-are-Misunderstood-and-Unappreciated.html
Beautiful article.
I get where you're coming from Most controller logic belongs to the domain
model so we created a domain model for this type of stuff, but the standard
everywhere I looked was to have "control" logic in the controller - what
about form and form validation etc? This is handled in the controller at the
moment. If I were to validate forms in the controller and validate data in
the model ( to keep it skinny), I'd be validating all over the place - so
I'd move my form into the Domain model.
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?
> 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.
> The has-a relationship works much better and tends to be more suitable for
> web applications
> which don't require or need a huge amount of modelling. Since it's outside
> the Model
> inheritance tree and included by composition instead (composition over
> inheritance!), it also
> makes it simple to Mock when performing testing. Quite useful if you're
> designing a Model and
> pushing TO the database level, rather than the common designing Models
> FROM an existing
> database schema.
Yeah composition over inheritance... but these ideas need to be applied
wisely instead of blindly being plastered onto anything that inherits
something from somewhere.
For example, inheriting from a class that is intended to be an abstract
class - Zend_Db_Table_Abstract, while tying you into the framework, provides
you with convenience.
You do know that you need to create a concrete child of the Zend_Db_Table
class to be able to use it right? it's an abstract class - so your TDG is
going to be inherited anyway - this is practically a point where a principle
like "composition over inheritance" does not apply too much unless of course
you intend to immediately apply domain model pattern which I think is
overkill for most web apps which just need to read the database and like I
said work as a proxy for the data source.
Anyway, we're straying a little here - I'm really interested in the reasons
why you guys have decided to shift to where you've shifted mainly because
I've made the decision to move away from the exact same thing. While Pad's
post somewhat gave me his angle as to why, I'm just really trying to
understand other peoples reasons.
Look forward to your replies.
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-tp20909700p20922870.html
Sent from the Zend MVC mailing list archive at Nabble.com.
没有评论:
发表评论