Skip to content
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

Conversation

NightJar
Copy link
Contributor

@NightJar NightJar commented May 20, 2018

Previously relationships defiend as many_many came in a special type of RelationList (ManyManyList) - however now this can be one of two types of RelationList depending on the type of definition, with both being valid many_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 a many_many through allows for this to be versioned via the intermediary join object, as with any other DataObject.

Resolves #254 and resolves #252

@NightJar
Copy link
Contributor Author

Left todo: Find a way to translate the related object ID into the intermediary object ID when setting the sort via reorderItems.
Unit tests for ManyManyThrough.

@robbieaverill robbieaverill changed the title WIP - NEW Add support for ManyManyThrough relations WIP: NEW Add support for ManyManyThrough relations May 21, 2018
@NightJar NightJar force-pushed the pulls/3.1/many-many-reordering-fix branch from 851b9b9 to 872b04f Compare May 31, 2018 22:33
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.
@NightJar
Copy link
Contributor Author

NightJar commented Jun 1, 2018

OK, it's a bit mess, but it seems to work :)
The mess can be addressed at another time: #261

@NightJar NightJar changed the title WIP: NEW Add support for ManyManyThrough relations NEW Add support for ManyManyThrough relations Jun 1, 2018
@thats4shaw
Copy link

I gave this a test as per #252 @NightJar.

Seems to work pretty well for me. The only hiccup I had was not versioning the joiner model initially. I'm also assuming in this scenario that the SortOrder field should just be defined in the joiner class rather than use $many_many_extraFields.

@NightJar
Copy link
Contributor Author

NightJar commented Jun 4, 2018

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 extraFields, but I assume not.

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).

Copy link
Contributor

@robbieaverill robbieaverill left a 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

* Checks to see if the relationship list is for a type of many_many
*
* @param SS_List $list
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing return type

$toRelationName = $manipulator->getLocalKey();
$sortlist = DataList::create($joinClass)->filter([
$toRelationName => $items->column('ID'),
$fromRelationName => $items->first()->getJoin()->$fromRelationName,
Copy link
Contributor

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?

Copy link
Contributor Author

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 returns) - which I'll also tighten up a touch.

$class = $this->getManyManyInspector($list)->getJoinClass();
$isVersioned = $class::create()->hasExtension(Versioned::class);

if ($listClassVersioned xor $isVersioned) {
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the opposite.

Copy link
Contributor

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

Copy link
Contributor Author

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 :)

@NightJar NightJar changed the title NEW Add support for ManyManyThrough relations WIP: NEW Add support for ManyManyThrough relations Jun 10, 2018
Copy link
Contributor

@ScopeyNZ ScopeyNZ left a 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.

// - Through object is versioned: handle as versioned
// - Through object is NOT versioned: THROW an error.
$isManyMany = $this->isManyMany($list);
if ($isManyMany && $list instanceof ManyManyThroughList) {
Copy link
Contributor

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.

$key = $list->getLocalKey();
$foreignKey = $list->getForeignKey();
if ($this->isManyMany($list)) {
$intropector = $this->getManyManyInspector($list);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$introspector or $inspector.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol 😅

@NightJar NightJar force-pushed the pulls/3.1/many-many-reordering-fix branch from 1ac7b66 to c98d710 Compare June 25, 2018 01:40
Copy link
Contributor

@robbieaverill robbieaverill left a 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

$intropector = $this->getManyManyInspector($list);
$extra = $list instanceof ManyManyList ?
$intropector->getExtraFields() :
DataObjectSchema::create()->fieldSpecs($intropector->getJoinClass(), DataObjectSchema::DB_ONLY);
Copy link
Contributor

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

* these functions are moved to ManyManyThroughQueryManipulator, but otherwise retain
* the same signature.
*
* @param ManyManyList|ManyManyThroughList
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing variable name

DefinerOne:
ID: 1
DefinerTwo:
ID: 2
Copy link
Contributor

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?

Copy link
Contributor Author

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 ],
Copy link
Contributor

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');
}
Copy link
Contributor

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;
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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 :)

Copy link
Contributor

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');
Copy link
Contributor

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(), ...

@NightJar NightJar force-pushed the pulls/3.1/many-many-reordering-fix branch 6 times, most recently from 08ed6f6 to 1eaa4c8 Compare June 25, 2018 03:36
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.
@NightJar NightJar force-pushed the pulls/3.1/many-many-reordering-fix branch from 1eaa4c8 to b6130c4 Compare June 26, 2018 02:41
@NightJar NightJar changed the title WIP: NEW Add support for ManyManyThrough relations NEW Add support for ManyManyThrough relations Jun 26, 2018
Copy link
Contributor

@robbieaverill robbieaverill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 😃 nice work!

@robbieaverill robbieaverill merged commit 7fdfe23 into symbiote:master Jul 1, 2018
@robbieaverill robbieaverill deleted the pulls/3.1/many-many-reordering-fix branch July 1, 2018 21:59
@gorriecoe
Copy link

When is this likely to be tagged? This fixes a major issue for our team so ideally this week if possible?

@robbieaverill
Copy link
Contributor

@gorriecoe here you go: https://github.com/symbiote/silverstripe-gridfieldextensions/releases/tag/3.2.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants