|
View:
New views
3 Messages
—
Rating Filter:
Alert me
|
|
|
Editing a Record with a HasManyThroughSince Alpha3 came out, I thought I would give it a bit of a workout
searching for bugs and the like. Basically, I am running scenarios that mimic the things we do on a daily basis. For this one, I am mimicking a blog database and updating a post. The post can have many categories. I am setting the post's categories at the same time as I add or edit the post without ajax-type events. Pretty straightforward. I just want to make sure that my methodology is sound. Much of the code I used from the Solar Bookmarks example. I have my tables set correctly and the relationships are fine. posts has many categories through posts_categories posts has many posts_categories categories has many posts through posts_categories categories has many posts_categories posts_categories belongs to posts posts_categories belongs to categories I am using a multiselect to pick my categories for the post. I set my form up to use a column called 'cats'. The options of this select are a categories->fetchPairs(). In my Posts model, I have a $_calculate_cols[] = 'cats'; I have two methods in the Posts record __setCats($vals) and __getCats() __setCats($vals) looks like this: if (! $this->categories) { $this->categories = $this->newRelated('categories'); } $this->categories->setCategoryIds($vals); $this->_data['cats'] = $this->categories->getCategoryIds(); __getCats() looks like this: if (empty($this->_data['cats'])) { if ($this->categories) { $this->_data['cats'] = $this->categories->getCategoryIds(); } } return $this->_data['cats']; I have two methods in my Categories Collection; getCategoryIds() and setCategoryIds($vals) getCategoryIds() looks like this: $clone = clone($this); $category_ids = array(); foreach ($clone as $category) { $category_ids[] = $category->category_id; } return $category_ids; setCategoryIds($vals) looks like this: $model = $this->getModel(); $vals = (array) $vals; $clone = clone($this); foreach ($clone as $key => $category) { if (! in_array($category->category_id, $vals)) { $this->removeOne($key); } } $remaining = $this->getCategoryIds(); foreach ($vals as $category_id) { if (! in_array($category_id, $remaining)) { $category = $model->fetch($category_id); if ($category) { $this[] = $category; } } } All of this seems to work fine and is pretty logical. I guess I am just wondering if this is the "proper" way to go about editing related many-to-many relationships on the fly. Is there a better way? Thanks, Jon _______________________________________________ Solar-talk mailing list Solar-talk@... http://mailman-mail5.webfaction.com/listinfo/solar-talk |
|
|
Re: Editing a Record with a HasManyThroughHi Jon,
Starting first with your last question: > All of this seems to work fine and is pretty logical. I guess I am > just wondering if this is the "proper" way to go about editing related > many-to-many relationships on the fly. Is there a better way? As far as I can tell from a once-over, you're doing it correctly for the code in trunk. Nice examples; thanks for writing them up. FYI, I am making some changes in branches/unfounded that will simplify some of the idioms used in these cases; read on for notes about the changes in branches/unfounded. > __setCats($vals) looks like this: > if (! $this->categories) { > $this->categories = $this->newRelated('categories'); > } > $this->categories->setCategoryIds($vals); > $this->_data['cats'] = $this->categories->getCategoryIds(); The branches/unfounded codebase makes it so that you don't need the `if (! $this->foo) { $this->foo = $this->newRelated('foo'); }` idiom any more. In your case, when using branches/unfounded, you can use $this->categories safely at all times, even if there were no related categories in the database already. The replacement code will look something like this: public function __setCats($vals) { $this->categories->setCategoryIds($vals); $this->_data['cats'] = $this->categories->getCategoryIds(); } The idea in branches/unfounded is, if a record or collection is not found, you no longer get back a null or array(). Instead, you get back an actual Record or Collection object, with a method called isEmpty(). Calling $record->isEmpty() or $collection->isEmpty() is the equivalent of calling empty($record) or empty($collection). An "empty" (not-found) record will not be saved, even if you call save() on it. However, The moment you attempt to set a value on the empty record, it converts itself to a "new" record, and so will be saved at the appropriate time. (The same goes for collections.) > __getCats() looks like this: > > if (empty($this->_data['cats'])) { > if ($this->categories) { > $this->_data['cats'] = $this->categories->getCategoryIds(); > } > } > return $this->_data['cats']; For the same reasons as above, under branches/unfounded, you can skip the `if ($this->categories)` check here as well. The replacement code would look like this: public function __getCats() { if (empty($this->_data['cats'])) { $this->_data['cats'] = $this->categories->getCategoryIds(); } return $this->_data['cats']; } > I have two methods in my Categories Collection; getCategoryIds() and > setCategoryIds($vals) > > getCategoryIds() looks like this: > > $clone = clone($this); > $category_ids = array(); > foreach ($clone as $category) { > $category_ids[] = $category->category_id; > } > return $category_ids; > > setCategoryIds($vals) looks like this: > > $model = $this->getModel(); > $vals = (array) $vals; > $clone = clone($this); > foreach ($clone as $key => $category) { > if (! in_array($category->category_id, $vals)) { > $this->removeOne($key); > } > } > $remaining = $this->getCategoryIds(); > foreach ($vals as $category_id) { > if (! in_array($category_id, $remaining)) { > $category = $model->fetch($category_id); > if ($category) { > $this[] = $category; > } > } > } N.b.: The getCategoryIds() method could be replaced, even in trunk, with a call to getColVals('category_id'). The method is implemented in Solar_Sql_Model_Collection. Regarding the need for clone(): I have some new code for branches/ unfounded, as yet uncommitted, that will make the clone() step here unnecessary. Instead of implementing Iterator, Solar_Struct will implement IteratorAggregate, which should eliminate the iteration conflicts that the clone() solution works around. As such, the replacement code might look something like this: public function getCategoryIds() { return $this->getColVals('category_id'); } public function setCategoryIds($vals) { $model = $this->getModel(); $vals = (array) $vals; foreach ($this as $key => $category) { if (! in_array($category->category_id, $vals)) { $this->removeOne($key); } } $remaining = $this->getCategoryIds(); foreach ($vals as $category_id) { if (! in_array($category_id, $remaining)) { $category = $model->fetch($category_id); /* note the use of isEmpty() below */ if (! $category->isEmpty()) { $this[] = $category; } } } } I can see a case for automating stuff like setCategoryIds() because its such a common need, but I am not sure yet how to go about it. Hope all this helps, and thanks again for taking the time to write up these examples. :-) -- Paul M. Jones http://paul-m-jones.com/ _______________________________________________ Solar-talk mailing list Solar-talk@... http://mailman-mail5.webfaction.com/listinfo/solar-talk |
|
|
Re: Editing a Record with a HasManyThroughPaul,
Thanks very much for taking the time to go through my code so thoroughly. It's a big help and I appreciate it. Sounds like lots of nice enhancements are forthcoming! It (Solar) gets better every day :) Jon On Mon, Sep 21, 2009 at 10:05 AM, Paul M Jones <pmjones@...> wrote: > Hi Jon, > > Starting first with your last question: > >> All of this seems to work fine and is pretty logical. I guess I am >> just wondering if this is the "proper" way to go about editing related >> many-to-many relationships on the fly. Is there a better way? > > As far as I can tell from a once-over, you're doing it correctly for > the code in trunk. Nice examples; thanks for writing them up. > > FYI, I am making some changes in branches/unfounded that will simplify > some of the idioms used in these cases; read on for notes about the > changes in branches/unfounded. > > >> __setCats($vals) looks like this: >> if (! $this->categories) { >> $this->categories = $this->newRelated('categories'); >> } >> $this->categories->setCategoryIds($vals); >> $this->_data['cats'] = $this->categories->getCategoryIds(); > > The branches/unfounded codebase makes it so that you don't need the > `if (! $this->foo) { $this->foo = $this->newRelated('foo'); }` idiom > any more. In your case, when using branches/unfounded, you can use > $this->categories safely at all times, even if there were no related > categories in the database already. The replacement code will look > something like this: > > public function __setCats($vals) > { > $this->categories->setCategoryIds($vals); > $this->_data['cats'] = $this->categories->getCategoryIds(); > } > > The idea in branches/unfounded is, if a record or collection is not > found, you no longer get back a null or array(). Instead, you get > back an actual Record or Collection object, with a method called > isEmpty(). Calling $record->isEmpty() or $collection->isEmpty() is the > equivalent of calling empty($record) or empty($collection). > > An "empty" (not-found) record will not be saved, even if you call > save() on it. However, The moment you attempt to set a value on the > empty record, it converts itself to a "new" record, and so will be > saved at the appropriate time. (The same goes for collections.) > > >> __getCats() looks like this: >> >> if (empty($this->_data['cats'])) { >> if ($this->categories) { >> $this->_data['cats'] = $this->categories->getCategoryIds(); >> } >> } >> return $this->_data['cats']; > > For the same reasons as above, under branches/unfounded, you can skip > the `if ($this->categories)` check here as well. The replacement code > would look like this: > > public function __getCats() > { > if (empty($this->_data['cats'])) { > $this->_data['cats'] = $this->categories->getCategoryIds(); > } > > return $this->_data['cats']; > } > > >> I have two methods in my Categories Collection; getCategoryIds() and >> setCategoryIds($vals) >> >> getCategoryIds() looks like this: >> >> $clone = clone($this); >> $category_ids = array(); >> foreach ($clone as $category) { >> $category_ids[] = $category->category_id; >> } >> return $category_ids; >> >> setCategoryIds($vals) looks like this: >> >> $model = $this->getModel(); >> $vals = (array) $vals; >> $clone = clone($this); >> foreach ($clone as $key => $category) { >> if (! in_array($category->category_id, $vals)) { >> $this->removeOne($key); >> } >> } >> $remaining = $this->getCategoryIds(); >> foreach ($vals as $category_id) { >> if (! in_array($category_id, $remaining)) { >> $category = $model->fetch($category_id); >> if ($category) { >> $this[] = $category; >> } >> } >> } > > N.b.: The getCategoryIds() method could be replaced, even in trunk, > with a call to getColVals('category_id'). The method is implemented > in Solar_Sql_Model_Collection. > > Regarding the need for clone(): I have some new code for branches/ > unfounded, as yet uncommitted, that will make the clone() step here > unnecessary. Instead of implementing Iterator, Solar_Struct will > implement IteratorAggregate, which should eliminate the iteration > conflicts that the clone() solution works around. > > As such, the replacement code might look something like this: > > public function getCategoryIds() > { > return $this->getColVals('category_id'); > } > > public function setCategoryIds($vals) > { > $model = $this->getModel(); > $vals = (array) $vals; > > foreach ($this as $key => $category) { > if (! in_array($category->category_id, $vals)) { > $this->removeOne($key); > } > } > > $remaining = $this->getCategoryIds(); > foreach ($vals as $category_id) { > if (! in_array($category_id, $remaining)) { > $category = $model->fetch($category_id); > /* note the use of isEmpty() below */ > if (! $category->isEmpty()) { > $this[] = $category; > } > } > } > } > > I can see a case for automating stuff like setCategoryIds() because > its such a common need, but I am not sure yet how to go about it. > > Hope all this helps, and thanks again for taking the time to write up > these examples. :-) > > > -- > > Paul M. Jones > http://paul-m-jones.com/ > > > > > _______________________________________________ > Solar-talk mailing list > Solar-talk@... > http://mailman-mail5.webfaction.com/listinfo/solar-talk > Solar-talk mailing list Solar-talk@... http://mailman-mail5.webfaction.com/listinfo/solar-talk |
| Free embeddable forum powered by Nabble | Forum Help |