If I were doing sites on the level that you are, I would be doing the same thing.pytrin wrote:Generally I agree. I'm just looking for more ways to prevent duplication and increase development speed
Building complex domain models
Moderator: General Moderators
- allspiritseve
- DevNet Resident
- Posts: 1174
- Joined: Thu Mar 06, 2008 8:23 am
- Location: Ann Arbor, MI (USA)
Re: Building complex domain models
Re: Building complex domain models
Ok, that sounds goodchris 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.
Code: Select all
function isValid() {
if (isset($this->_author) && !$this->_author->isValid())
{
return false;
}
// ... normal validation logic here
}
Code: Select all
function getAuthor() {
return $this->referenceMap->get ('author')->getById ($this->author_id);
}
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
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.
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.
- allspiritseve
- DevNet Resident
- Posts: 1174
- Joined: Thu Mar 06, 2008 8:23 am
- Location: Ann Arbor, MI (USA)
Re: Building complex domain 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: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.
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();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.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).
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);
}
}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.
- inghamn
- Forum Contributor
- Posts: 174
- Joined: Mon Apr 16, 2007 10:33 am
- Location: Bloomington, IN, USA
Re: Building complex domain models
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.
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.
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();
Re: Building complex domain models
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
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.
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.
Yes, I remember well and your approach of asking models to validate themselves is definitely the way I want it to workpytrin 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.
- allspiritseve
- DevNet Resident
- Posts: 1174
- Joined: Thu Mar 06, 2008 8:23 am
- Location: Ann Arbor, MI (USA)
Re: Building complex domain models
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.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();
Re: Building complex domain models
Then you override the default limiting functionality.
- inghamn
- Forum Contributor
- Posts: 174
- Joined: Mon Apr 16, 2007 10:33 am
- Location: Bloomington, IN, USA
Re: Building complex domain models
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.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.
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
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.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.