Page 2 of 2

Re: Building complex domain models

Posted: Sun Jan 04, 2009 1:14 am
by allspiritseve
pytrin wrote:Generally I agree. I'm just looking for more ways to prevent duplication and increase development speed :)
If I were doing sites on the level that you are, I would be doing the same thing.

Re: Building complex domain models

Posted: Sun Jan 04, 2009 2:36 am
by matthijs
chris wrote:I don't use "separate" setters for just the related domain objects, I use setters and getters for all domain object fields, so there's no inconsistency in my interface. Some fields take numbers, some take strings, some take domain objects. It depends on what the field represents.
Ok, that sounds good

Code: Select all

 
function isValid() {
  if (isset($this->_author) && !$this->_author->isValid())
  {
    return false;
  }
  // ... normal validation logic here
 
}
 
So in this case, $this->_author would be a field property of your model, but unlike other fields which could be a string or an array this field is a complete domain object, am I right in understanding that? It sounds like I want to go in that direction.

Code: Select all

 
function getAuthor()    {
    return $this->referenceMap->get ('author')->getById ($this->author_id);
}
 
This only works when you already have the author_id. My problem is that I start with an author name. So the model you're working with, Post, gets data (posttitle, postbody, author, categories, tags), and has to deal with all that data. But preferably without replicating all the domain logic of the Author, Category and Tag models.

Say a new post is posted, complete with a whole bunch of tags. Now for each of those tags you have to check if they already exist, if so then get the tag_id's belonging to those, if not insert them in the tag db table and get the tag_id's, then insert those tag_id's in the reference table posts_tags, etc. That's a lot of logic, and it's difficult to find ways to deal with that in a clean way (not repeat everything all over the place).

pytrins your reference map looks like an interesting solution

Re: Building complex domain models

Posted: Sun Jan 04, 2009 4:23 am
by Eran
I basically started with the Zend reference map when it was used only for implementing database constraints in the application. I think now they have a similar approach for retrieving related datasets.

Regarding validation, I think we talked about it in skeleton before. I use arrays of filters/validators in each model, and ask models to validate themselves as needed. If post needs to validate tags, it asks the tags model to do so. I also wrote a custom validator that checks if a unique value exists in the database.

Re: Building complex domain models

Posted: Sun Jan 04, 2009 9:23 am
by allspiritseve
matthijs wrote:This only works when you already have the author_id. My problem is that I start with an author name. So the model you're working with, Post, gets data (posttitle, postbody, author, categories, tags), and has to deal with all that data. But preferably without replicating all the domain logic of the Author, Category and Tag models.
I wasn't really intending it to work, I was just trying to understand Pytrin's method. It sounds to me like, for some reason, you have unpersisted data for both a Post and an Author. In that case, I would do something like this:

Code: Select all

$author = new User();
$author->setName ($_POST['author_name']);
$author->save();
 
$post = new Post();
$post->setTitle ($_POST['title']);
$post->setBody ($_POST['body']);
$post->setAuthor ($author);
$post->save();
Keep in mind I don't use ActiveRecord, so i may be doing something wrong.
matthijs wrote:Say a new post is posted, complete with a whole bunch of tags. Now for each of those tags you have to check if they already exist, if so then get the tag_id's belonging to those, if not insert them in the tag db table and get the tag_id's, then insert those tag_id's in the reference table posts_tags, etc. That's a lot of logic, and it's difficult to find ways to deal with that in a clean way (not repeat everything all over the place).
That sounds to me like a perfect candidate for a Gateway or a Mapper, and not ActiveRecord. In my opinion you're seeing where AR falls apart... dealing with complex relationships. Others may argue on that point, and maybe I just don't know of the proper way to handle them with AR, but I find separating the persistence logic from the domain logic much cleaner.

For your tag example, here are is an idea:

Client Code:

Code: Select all

$post = new Post();
foreach ($_POST['tags'] as $tag):
$post->addTag (new Tag ($tag));
endforeach;
$postsMapper->save (Post $post);
 
PostsMapper:

Code: Select all

function save (Post $post)  {
    $this->mapperRegistry->get ('PostTag')->saveTagsForPost ($post->getTags(), $post);
}
}
 
PostTagsMapper

Code: Select all

function saveTagsForPost (Collection $tags, Post $post) {
    $postTags = new Collection();
    foreach ($tags as $tag):
    $postTags->add (new PostTag ($post, $this->mapperRegistry->get ('Tag')->save ($tag)));
    endforeach;
    $this->save ($postTags);
}
}
Each mapper has its own responsibility, and when it can't handle something, it passes it off to another mapper. The PostMapper gets a Post, and sends its Tags to PostTagMapper, which maps the relationship between Posts and Tags. (PostTag is just a value object... not a full fledged domain object). PostTagMapper doesn't know if the post exists, and doesn't care... it needs to exist. So it sends the tag off to TagMapper, which takes care of deciding whether or not a given Tag exists. If it does, great. If not, create it. Back in the PostTagMapper, it receives a fully-persisted Tag and adds it to a collection of PostTags. When it saves, it just deletes all the other PostTags in the database (since they're value objects) and inserts each PostTag anew.

Maybe that's not what you want... but I would have killed for datamapper examples like this a year ago. If it inspires somebody, then I'm happy.

Re: Building complex domain models

Posted: Sun Jan 04, 2009 9:54 am
by inghamn
For what it's worth (which may not be much), Active Record is still working very well for all the stuff I've been building. I keep the SQL and the business logic all in one class. I haven't had the need, yet to split them up.

I do however, use seperate classes to deal with collections of ActiveRecord objects. So I end up having both a Post class and a PostList class. Granted, a DataMapper style would alleviate the need for sererate classes. However, I really like clean code ActiveRecord lets me use in my controllers. I much prefer simple controllers, and hide the complexity in the model layer.

Code: Select all

 
// To me this makes more sense, when writing controllers.
$post = new Post();
foreach($_POST['post'] as $field=>$value)
{
    $set = 'set'.ucfirst($field);
    $post->$set($value);
}
$post->save();
 
To speed up development, I've written code generators to use the database to create the starting point for all the models, controllers, and views.

Re: Building complex domain models

Posted: Sun Jan 04, 2009 10:33 am
by Eran
To me this makes more sense:

Code: Select all

//All the logic that needs to be in the controller to add a new post
$posts = new Posts();
$result = $posts -> insertValid($_POST);

Re: Building complex domain models

Posted: Sun Jan 04, 2009 2:43 pm
by matthijs
Great replies guys, thanks. Family business today so I haven't had a chance to read your posts until now.

I agree with pytrin, that a controller method like that would be the nicest.

inghamn and allspiritseve: I'll study your examples more tomorrow, but from what I see now it already gives some good ideas. Your mapper idea is certainly worth exploring more, allspiritseve.

inghamn, the active record is something I would use, but I get in trouble as soon as there are more complex relations between model objects. But for the more basic objects, like User or Country AR works well.
pytrin wrote:Regarding validation, I think we talked about it in skeleton before. I use arrays of filters/validators in each model, and ask models to validate themselves as needed. If post needs to validate tags, it asks the tags model to do so.
Yes, I remember well and your approach of asking models to validate themselves is definitely the way I want it to work

Re: Building complex domain models

Posted: Sun Jan 04, 2009 3:00 pm
by allspiritseve
inghamn wrote:

Code: Select all

 
// To me this makes more sense, when writing controllers.
$post = new Post();
foreach($_POST['post'] as $field=>$value)
{
    $set = 'set'.ucfirst($field);
    $post->$set($value);
}
$post->save();
 
What happens if your method names are different than the array keys? For example, if I'm adding a tag to an item, I use addTag ($tag), not setTag ($tag). Or a method like setAuthorID when the field was "author_id". I guess the automation just feels very limiting.

Re: Building complex domain models

Posted: Sun Jan 04, 2009 10:02 pm
by josh
Then you override the default limiting functionality.

Re: Building complex domain models

Posted: Mon Jan 05, 2009 8:21 am
by inghamn
allspiritsteve wrote: What happens if your method names are different than the array keys? For example, if I'm adding a tag to an item, I use addTag ($tag), not setTag ($tag). Or a method like setAuthorID when the field was "author_id". I guess the automation just feels very limiting.
Since you're the one writing the methods, and you're the one writing the form fields, there's no reason they wouldn't match. The method names and the form field names are determined by you.

One thing to keep in mind...the need to call addTag() is a case where the user is entering one one tag at a time, and would have completely different form fields from setTags(). Using an addTag() method denotes the user using a single screen form to add one tag, then returning to view the post before attempting to add another tag. setTags() denotes typing all the tags in at once....usually in a plain text input. Both are legitimate ways to work with it, and you would quite possible have both available to be called. But a controller calling setTags() is most likely not going to be calling addTag(). Which means the naming scheme would still be useful.

I tend to work from the user interface side first. I get the forms and user experience designed first, then make my models' methods accomodate that. Which means a lot of my models' methods are written to specifically make the controllers easier.

Re: Building complex domain models

Posted: Tue Jan 06, 2009 1:16 am
by josh
inghamn wrote:Since you're the one writing the methods, and you're the one writing the form fields, there's no reason they wouldn't match. The method names and the form field names are determined by you.
Well thats kind of the point of DM, is to let the schemas be uncoupled, putting persistence logic in a separate layer is kind of a side effect, a means to an end not an end of it's own.