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

Artist role (autor) - parse from artist name and store in separately #898

Merged
merged 4 commits into from
Sep 22, 2023

Conversation

rastislav-chynoransky
Copy link
Contributor

Copy link
Contributor

@eronisko eronisko left a comment

Choose a reason for hiding this comment

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

I'm sure this code works but I'm having a hard time understanding it. Could you please take a look at the suggestions and/or could we have chat about this?

'authority_item',
'item_id',
'authority_id'
)->withPivot(['role', 'automatically_matched']);
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this work without selecting automatically_matched globally?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I find it more convenient to have it listed globally especially when one boolean attribute creates not much of overhead.

Copy link
Contributor

Choose a reason for hiding this comment

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

I get the convenience aspect but I'd still suggest not including it globally. The problem with these global includes is that they are very difficult to track down if we ever want to change or remove that column later on. Having to say withPivot() is annoying but it is much more explicit.

This is coming from experience, I've used this feature many times and it always bit me in the end 😄

app/Item.php Outdated
Comment on lines 808 to 823
{
$idsToDetach = $this->authorities
->filter(fn(Authority $authority) => $authority->pivot->automatically_matched)
->pluck('id')
->diff($idsWithPivotData->keys());

$this->authorities()->detach($idsToDetach);
$this->authorities()->syncWithoutDetaching(
$idsWithPivotData->map(
fn($pivotData) => [
'automatically_matched' => true,
...$pivotData,
]
)
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to make sure I understand: this removes all automatically_matched authorities that are not present in $idsWithPivotData keys?

Would this work too?

Suggested change
{
$idsToDetach = $this->authorities
->filter(fn(Authority $authority) => $authority->pivot->automatically_matched)
->pluck('id')
->diff($idsWithPivotData->keys());
$this->authorities()->detach($idsToDetach);
$this->authorities()->syncWithoutDetaching(
$idsWithPivotData->map(
fn($pivotData) => [
'automatically_matched' => true,
...$pivotData,
]
)
);
}
{
$this->authorities()->wherePivot('automatically_matched', true)->sync(
$idsWithPivotData->map(
fn($pivotData) => [
'automatically_matched' => true,
...$pivotData,
]
)
);
}

If so, could it be inlined instead? (I can only see it used once)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This approach won't work because the wherePivot condition will limit the scope of pivot records, effectively leading to inserts of duplicate pivots if there already exist some with automatically_matched = false

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And for now this change is made only for GMUHK gallery, however it will get reused in CSV importers (or any new data source).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@eronisko I've included your suggestion discussed via slack 84c5076

@@ -184,4 +186,44 @@ protected function createFreeItem()
'date_latest' => 1, // CE
]);
}

public function testSyncMatchedAuthorities()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm very happy to see a test here but I'm having trouble understanding what it does :(

Could you perhaps work it into a more higher-level test case for GmuhkItemImporter?

Maybe a test like this

public function testParseRoleFromArtistName() 
{
    $data = [...];
    GmuhkItemImporter::import($data);
    $this->assertEquals('expected role', Item::first()->authorities[0]->role)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In fact the test makes multiple assertions and could be split into five scenarios:

  • automatically matched that are present in newly matched are expected to stay there (with updated pivot data)
  • manually matched that are present in newly matched are expected to stay there (with updated pivot data)
  • automatically matched that are missing from newly matched are expected to be deleted
  • manually matched that are missing from newly matched are expected to stay there
  • and finally any new matches should be created

Of course I can create a higher-level test for importing role, but the test of these relationship changes should stay on the model-level.

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 higher-level test 892ca5b

Copy link
Contributor

@eronisko eronisko left a comment

Choose a reason for hiding this comment

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

I've got a couple more suggestions with the global withPivot being a serious concern. Otherwise, this is good to go, thank you.

'authority_item',
'item_id',
'authority_id'
)->withPivot(['role', 'automatically_matched']);
Copy link
Contributor

Choose a reason for hiding this comment

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

I get the convenience aspect but I'd still suggest not including it globally. The problem with these global includes is that they are very difficult to track down if we ever want to change or remove that column later on. Having to say withPivot() is annoying but it is much more explicit.

This is coming from experience, I've used this feature many times and it always bit me in the end 😄

Comment on lines +34 to +45
Authority::factory()->create([
'name' => 'Miroslav Podhrázský',
'birth_year' => null,
'death_year' => null,
]);

$row = FakeRecordFactory::buildGmuhkItem([
'author' => ['Miroslav Podhrázský (fotograf)'],
]);
$item = $this->importer->import($row, new Progress());
$item->refresh();
$this->assertEquals('fotograf', $item->authorities[0]->pivot->role);
Copy link
Contributor

Choose a reason for hiding this comment

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

Very nice, thank you

Comment on lines +807 to +809
public function syncMatchedAuthorities(\Illuminate\Support\Collection $idsWithPivotData)
{
// Detach automatically-matched authorities that no longer match
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for this, just last suggestion to accept array as well (in order to be more in-line with Laravel's sync method)

Suggested change
public function syncMatchedAuthorities(\Illuminate\Support\Collection $idsWithPivotData)
{
// Detach automatically-matched authorities that no longer match
public function syncMatchedAuthorities(\Illuminate\Support\Collection|array $idsWithPivotData)
{
$idsWithPivotData = collect($idsWithPivotData);
// Detach automatically-matched authorities that no longer match

Comment on lines +192 to +227
$authorities = Authority::factory()
->count(5)
->create();
$item = Item::factory()->create();
$item->authorities()->sync([
// present in matched authorities
$authorities[0]->id => ['role' => 'author', 'automatically_matched' => true],
$authorities[1]->id => ['role' => 'author', 'automatically_matched' => false],
// missing from matched authorities
$authorities[2]->id => ['role' => 'author', 'automatically_matched' => true],
$authorities[3]->id => ['role' => 'author', 'automatically_matched' => false],
]);

$item->syncMatchedAuthorities(
collect([
$authorities[0]->id => ['role' => 'after'],
$authorities[1]->id => ['role' => 'after'],
$authorities[4]->id => ['role' => 'after'],
])
);

$item->refresh();
$this->assertCount(4, $item->authorities);
$this->assertEquals(
[
$authorities[0]->id => ['role' => 'after', 'automatically_matched' => true],
$authorities[1]->id => ['role' => 'after', 'automatically_matched' => false],
$authorities[3]->id => ['role' => 'author', 'automatically_matched' => false],
$authorities[4]->id => ['role' => 'after', 'automatically_matched' => true],
],
$item->authorities
->pluck('pivot')
->keyBy('authority_id')
->map->only(['role', 'automatically_matched'])
->toArray()
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Here's a more chatty version. I find it a bit easier to digest but I'm not 100% happy with this either. Feel free to use/adapt it or throw away.

Suggested change
$authorities = Authority::factory()
->count(5)
->create();
$item = Item::factory()->create();
$item->authorities()->sync([
// present in matched authorities
$authorities[0]->id => ['role' => 'author', 'automatically_matched' => true],
$authorities[1]->id => ['role' => 'author', 'automatically_matched' => false],
// missing from matched authorities
$authorities[2]->id => ['role' => 'author', 'automatically_matched' => true],
$authorities[3]->id => ['role' => 'author', 'automatically_matched' => false],
]);
$item->syncMatchedAuthorities(
collect([
$authorities[0]->id => ['role' => 'after'],
$authorities[1]->id => ['role' => 'after'],
$authorities[4]->id => ['role' => 'after'],
])
);
$item->refresh();
$this->assertCount(4, $item->authorities);
$this->assertEquals(
[
$authorities[0]->id => ['role' => 'after', 'automatically_matched' => true],
$authorities[1]->id => ['role' => 'after', 'automatically_matched' => false],
$authorities[3]->id => ['role' => 'author', 'automatically_matched' => false],
$authorities[4]->id => ['role' => 'after', 'automatically_matched' => true],
],
$item->authorities
->pluck('pivot')
->keyBy('authority_id')
->map->only(['role', 'automatically_matched'])
->toArray()
);
$item = Item::factory()->create();
[$authority1, $authority2] = Authority::factory()
->count(2)
->create();
// Updates existing authorities
$item->authorities()->sync([
$authority1->id => ['role' => 'author', 'automatically_matched' => true],
$authority2->id => ['role' => 'author', 'automatically_matched' => false],
]);
$item->syncMatchedAuthorities([
$authority1->id => ['role' => 'new-role-1'],
$authority2->id => ['role' => 'new-role-2'],
]);
$this->assertEqualsCanonicalizing(
collect(['new-role-1', 'new-role-2']),
$item->authorities()->pluck('role')
);
// Deletes auto-matched authorities, keeps manually matched
$item->authorities()->sync([
$authority1->id => ['automatically_matched' => true],
$authority2->id => ['automatically_matched' => false],
]);
$item->syncMatchedAuthorities(collect([]));
$this->assertCount(1, $item->authorities()->get());
$this->assertEquals($authority2->id, $item->authorities()->first()->id);
// Adds new authorities
$item->authorities()->sync([]);
$item->syncMatchedAuthorities([
$authority1->id => ['role' => 'author'],
]);
$this->assertCount(1, $item->authorities()->get());

@rastislav-chynoransky rastislav-chynoransky merged commit 2cf05dc into develop Sep 22, 2023
1 check passed
@rastislav-chynoransky rastislav-chynoransky deleted the WEBUMENIA-1533 branch September 22, 2023 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants