-
Notifications
You must be signed in to change notification settings - Fork 125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NEW Add support for ManyManyThrough relations #260
NEW Add support for ManyManyThrough relations #260
Conversation
Left todo: Find a way to translate the related object ID into the intermediary object ID when setting the sort via |
851b9b9
to
872b04f
Compare
Previously relationships defiend as many_many came in a special type of RelationList - however now this can be one of two types of RelationList depending on the type of definition, with both being valid many_many relationships. This had the unfortunate side effect of seeing the OrderableRows component in (the least) cease functioning correctly. No longer. This also has the fortunate bonus of allowing a many_many relationship to be versioned; where previously while each item in the relationship could be versioned, the relationship itself could not.
872b04f
to
9fa9ef8
Compare
OK, it's a bit mess, but it seems to work :) |
Hi @thats4shaw, thanks for giving this a go! Yes you're right, it should now be defined on the intermediary model - I've not looked into whether or not that would work with I inserted some errors to be thrown on the case of mis-matched versioning strategies between the intermediary and the related model - but if I'm honest it's mostly because it hurt my brain trying to think through what it would mean if I didn't. I figure if it remains or becomes an issue it could be dealt with separately (in another issue). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of minor things and questions - this change definitely needs some unit tests too since its changing quite a lot of important logic
src/GridFieldOrderableRows.php
Outdated
* Checks to see if the relationship list is for a type of many_many | ||
* | ||
* @param SS_List $list | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing return type
src/GridFieldOrderableRows.php
Outdated
$toRelationName = $manipulator->getLocalKey(); | ||
$sortlist = DataList::create($joinClass)->filter([ | ||
$toRelationName => $items->column('ID'), | ||
$fromRelationName => $items->first()->getJoin()->$fromRelationName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if ->first()
returns null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Safe due to earlier checks (short circuit return
s) - which I'll also tighten up a touch.
src/GridFieldOrderableRows.php
Outdated
$class = $this->getManyManyInspector($list)->getJoinClass(); | ||
$isVersioned = $class::create()->hasExtension(Versioned::class); | ||
|
||
if ($listClassVersioned xor $isVersioned) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this implying that the base class and a MMT that is owns can't both be versioned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the opposite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"if the list class is versioned or (never and) the data class versioned"
I'm confused
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If one is versioned and the other is not, throw an error.
I'll add a comment :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super minor feedback not really blockers for approval I guess.
src/GridFieldOrderableRows.php
Outdated
// - Through object is versioned: handle as versioned | ||
// - Through object is NOT versioned: THROW an error. | ||
$isManyMany = $this->isManyMany($list); | ||
if ($isManyMany && $list instanceof ManyManyThroughList) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if($isManyMany)
is completely redundant here.
src/GridFieldOrderableRows.php
Outdated
$key = $list->getLocalKey(); | ||
$foreignKey = $list->getForeignKey(); | ||
if ($this->isManyMany($list)) { | ||
$intropector = $this->getManyManyInspector($list); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$introspector
or $inspector
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol 😅
1ac7b66
to
c98d710
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor comments. I'll pull this in and give it a test now
src/GridFieldOrderableRows.php
Outdated
$intropector = $this->getManyManyInspector($list); | ||
$extra = $list instanceof ManyManyList ? | ||
$intropector->getExtraFields() : | ||
DataObjectSchema::create()->fieldSpecs($intropector->getJoinClass(), DataObjectSchema::DB_ONLY); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multi line ternary isn't the best for readability, perhaps you could make this an if statement? Failing that, putting the ?
and :
at the start of lines helps to indicate that it's part of a ternary condition and not just bad indentation
src/GridFieldOrderableRows.php
Outdated
* these functions are moved to ManyManyThroughQueryManipulator, but otherwise retain | ||
* the same signature. | ||
* | ||
* @param ManyManyList|ManyManyThroughList |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing variable name
tests/OrderableRowsThroughTest.yml
Outdated
DefinerOne: | ||
ID: 1 | ||
DefinerTwo: | ||
ID: 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Defining IDs in fixtures isn't a very good idea - you'd ideally let the DB driver assign them via auto increments. Maybe just define a simple varchar for the sake of fixturing instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out you don't need to define any values, only names is sufficient :)
protected static $required_extensions = [ | ||
ThroughDefiner::class => [ Versioned::class ], | ||
ThroughIntermediary::class => [ Versioned::class ], | ||
ThroughBelongs::class => [ Versioned::class ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you remove the spacing around the array values?
foreach ($parent->Belongings() as $item) { | ||
if ($item->stagesDiffer()) { | ||
$this->fail('Unexpected diference found on stages'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps you could switch this if statement for $this->assertFalse($item->stagesDiffer(), 'No difference found on stages');
(note typo)
$this->assertEquals($desiredOrder, $newOrder); | ||
|
||
// pass forward to testReorderedPublish | ||
return $parent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think is happening, and if it were I think I'd suggest it'd be better not to couple unit tests together. I see you've declared testReorderedPublish
as depending on this test, but it doesn't actually use its return value - can you remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, can't do. It is passed in at line 86, and used on line 90, 98, & 101 (in testReorderedPublish
). I agree that normally it's not a good idea to string tests along like this, but in this case I feel it's acceptable in that if the first test fails there's no point in testing the second as it truly is a prerequisite (in order to publish one must be able to save) - I'm not aware of any method to fixture version information.
It could also be argued that this could all be part of one big test if this dependency is the case, but I feel it is cleaner this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added docblock @param
for testReorderedPublish
to compliment the comment above the return there and make it a bit clearer :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, the return value is passed into testReorderedPublish as an arg. Ok
// there should be no difference between stages anymore | ||
foreach ($parent->Belongings() as $item) { | ||
if ($item->stagesDiffer()) { | ||
$this->fail('Unexpected diference found on stages'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment re: assertFalse($item->stagesDiffer(), ...
08ed6f6
to
1eaa4c8
Compare
The previous commit (9fa9ef8) added support for the new SilverStripe 4 feature of Many Many relationships through an intermediary object. After much head scratching and community testing, the solution was proven to work, however had no automated tests to confirm as such. This commit rectifies that by testing both versioned and unversioned DataObjects in a many_many through style relationship. Some minor tidy and comments were also added as per feedback on the functionality code changes.
1eaa4c8
to
b6130c4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 😃 nice work!
When is this likely to be tagged? This fixes a major issue for our team so ideally this week if possible? |
Previously relationships defiend as
many_many
came in a special type ofRelationList
(ManyManyList
) - however now this can be one of two types ofRelationList
depending on the type of definition, with both being validmany_many
relationships (i.e.ManyManyThrough
).This had the unfortunate side effect of seeing the OrderableRows component in (the least) cease functioning correctly.
This also addresses the inability to version many_many releationships (i.e.
many_many_extraFields
- e.g. ordering), as the join table itself was not versioned. Using amany_many through
allows for this to be versioned via the intermediary join object, as with any otherDataObject
.Resolves #254 and resolves #252