Skip to content

Commit f04bff4

Browse files
NorguljmiloticStyleCIBot
authored
Unique values per custom field #minor (#74)
* Adjusted seeders so that it seedes unique values per custom field * Added unique constraint to selection type values table * Added accessor for casting selection custom field values * Apply fixes from StyleCI * Seeder fix * Extracted fake values to trait + minor * Apply fixes from StyleCI * Minor alignment + fixes * Common lock update Co-authored-by: jmilotic <josip.milotic@asseco-see.hr> Co-authored-by: StyleCI Bot <bot@styleci.io>
1 parent 394d648 commit f04bff4

File tree

8 files changed

+177
-76
lines changed

8 files changed

+177
-76
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"require": {
66
"php": "^7.4 || ^8.0",
77
"laravel/framework": "^8.0",
8-
"asseco-voice/laravel-common": "~0.1"
8+
"asseco-voice/laravel-common": "^1.0"
99
},
1010
"require-dev": {
1111
"phpunit/phpunit": "9.4",

composer.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\DB;
6+
use Illuminate\Support\Facades\Schema;
7+
8+
class AddUniqueIndexToCustomFieldSelectionValuesTable extends Migration
9+
{
10+
/**
11+
* Run the migrations.
12+
*
13+
* @return void
14+
*/
15+
public function up()
16+
{
17+
$this->consolidate();
18+
19+
Schema::table('custom_field_selection_values', function (Blueprint $table) {
20+
$table->unique(['selection_type_id', 'value']);
21+
});
22+
}
23+
24+
/**
25+
* Reverse the migrations.
26+
*
27+
* @return void
28+
*/
29+
public function down()
30+
{
31+
Schema::table('custom_field_selection_values', function (Blueprint $table) {
32+
$table->dropForeign(['selection_type_id']);
33+
});
34+
35+
Schema::table('custom_field_selection_values', function (Blueprint $table) {
36+
$table->dropUnique(['selection_type_id', 'value']);
37+
});
38+
39+
Schema::table('custom_field_selection_values', function (Blueprint $table) {
40+
$table
41+
->foreign('selection_type_id')
42+
->references('id')
43+
->on('custom_field_selection_types')
44+
->cascadeOnDelete();
45+
});
46+
}
47+
48+
protected function consolidate()
49+
{
50+
$selectionValues = DB::table('custom_field_selection_values')->get();
51+
52+
foreach ($selectionValues as $selectionValue) {
53+
if (DB::table('custom_field_selection_values')->where('id', $selectionValue->id)->exists()) {
54+
$ids = DB::table('custom_field_selection_values')
55+
->where('selection_type_id', $selectionValue->selection_type_id)
56+
->where('value', $selectionValue->value)
57+
->where('id', '!=', $selectionValue->id)
58+
->pluck('id');
59+
60+
DB::table('custom_field_selection_values')->whereIn('id', $ids)->delete();
61+
}
62+
}
63+
}
64+
}

src/App/Models/SelectionValue.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Illuminate\Database\Eloquent\Factories\HasFactory;
1010
use Illuminate\Database\Eloquent\Model;
1111
use Illuminate\Database\Eloquent\Relations\BelongsTo;
12+
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
1213

1314
class SelectionValue extends Model implements \Asseco\CustomFields\App\Contracts\SelectionValue
1415
{
@@ -27,4 +28,38 @@ public function selectionType(): BelongsTo
2728
{
2829
return $this->belongsTo(get_class(app(SelectionType::class)));
2930
}
31+
32+
public function type(): HasOneThrough
33+
{
34+
return $this->hasOneThrough(
35+
get_class(app(PlainType::class)),
36+
get_class(app(SelectionType::class)),
37+
'id',
38+
'id',
39+
'selection_type_id',
40+
'plain_type_id'
41+
);
42+
}
43+
44+
/**
45+
* Accessor for casting value to appropriate type based on the actual plain type.
46+
*
47+
* @param $value
48+
* @return bool|float|int
49+
*/
50+
public function getValueAttribute($value)
51+
{
52+
$plainType = optional($this->type)->name;
53+
54+
switch ($plainType) {
55+
case 'integer':
56+
return (int) $value;
57+
case 'float':
58+
return (float) $value;
59+
case 'boolean':
60+
return (bool) $value;
61+
default:
62+
return $value;
63+
}
64+
}
3065
}

src/App/Traits/FakesTypeValues.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Asseco\CustomFields\App\Traits;
4+
5+
use Faker\Generator;
6+
7+
trait FakesTypeValues
8+
{
9+
protected function fakeValueFromType($type, Generator $faker)
10+
{
11+
switch ($type) {
12+
case 'integer':
13+
return $faker->unique()->randomNumber();
14+
case 'float':
15+
return $faker->unique()->randomFloat();
16+
case 'date':
17+
return $faker->unique()->date();
18+
case 'time':
19+
return $faker->unique()->time();
20+
case 'datetime':
21+
return $faker->unique()->datetime();
22+
case 'text':
23+
return $faker->unique()->sentence;
24+
case 'boolean':
25+
return $faker->unique()->boolean;
26+
default:
27+
return $faker->unique()->word;
28+
}
29+
}
30+
}

src/Database/Seeders/SelectionValueSeeder.php

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,70 +6,61 @@
66

77
use Asseco\CustomFields\App\Contracts\SelectionType;
88
use Asseco\CustomFields\App\Contracts\SelectionValue;
9+
use Asseco\CustomFields\App\Traits\FakesTypeValues;
910
use Faker\Factory;
1011
use Faker\Generator;
1112
use Illuminate\Database\Seeder;
1213
use Illuminate\Support\Str;
1314

1415
class SelectionValueSeeder extends Seeder
1516
{
17+
use FakesTypeValues;
18+
1619
public function run(): void
1720
{
18-
/** @var SelectionType $selectionType */
19-
$selectionType = app(SelectionType::class);
21+
/** @var SelectionType $selectionTypeClass */
22+
$selectionTypeClass = app(SelectionType::class);
2023
/** @var SelectionValue $selectionValueClass */
2124
$selectionValueClass = app(SelectionValue::class);
2225

2326
$faker = Factory::create();
24-
$amount = 50;
25-
$selectionTypes = $selectionType::with('type')->get();
27+
$selectionTypes = $selectionTypeClass::with('type')->whereDoesntHave('values')->get();
2628

2729
$selectionValues = [];
28-
for ($i = 0; $i < $amount; $i++) {
29-
30-
// Have random number of values set for a single type
31-
$random = rand(3, 10);
32-
30+
foreach ($selectionTypes as $selectionType) {
31+
// Merging to enable single insert for performance reasons
3332
$selectionValues = array_merge_recursive($selectionValues,
34-
$selectionValueClass::factory()->count($random)->make()
35-
->each(function (SelectionValue $selectionValue) use ($selectionTypes, $faker) {
36-
if (config('asseco-custom-fields.migrations.uuid')) {
37-
$selectionValue->id = Str::uuid();
38-
}
33+
$this->makeValue($selectionValueClass, $selectionType, $faker)
34+
);
3935

40-
$selectionType = $selectionTypes->random(1)->first();
36+
// We do this to reset fakers unique value list, so that
37+
// values are unique per custom field instead of database
38+
$faker->unique(true);
39+
}
4140

42-
$selectionValue->timestamps = false;
43-
$selectionValue->selection_type_id = $selectionType->id;
44-
$selectionValue->value = $this->getTypeValue($selectionType, $faker);
45-
})->toArray()
46-
);
41+
// When comparing type name, this gets appended to the model
42+
// which breaks the insert if not removed
43+
foreach ($selectionValues as &$selectionValue) {
44+
unset($selectionValue['type']);
4745
}
4846

4947
$selectionValueClass::query()->insert($selectionValues);
5048
}
5149

52-
protected function getTypeValue(SelectionType $selectionType, Generator $faker)
50+
protected function makeValue(SelectionValue $selectionValueClass, SelectionType $selectionType, Generator $faker): array
5351
{
54-
$plainType = $selectionType->type->name;
52+
// Have random number of values set for a single type, 2 for boolean plain type
53+
$number = $selectionType->type->name === 'boolean' ? 2 : rand(3, 10);
5554

56-
switch ($plainType) {
57-
case 'integer':
58-
return $faker->randomNumber();
59-
case 'float':
60-
return $faker->randomFloat();
61-
case 'date':
62-
return $faker->date();
63-
case 'time':
64-
return $faker->time();
65-
case 'datetime':
66-
return $faker->datetime();
67-
case 'text':
68-
return $faker->sentence;
69-
case 'boolean':
70-
return $faker->boolean;
71-
default:
72-
return $faker->word;
73-
}
55+
return $selectionValueClass::factory()->count($number)->make()
56+
->each(function (SelectionValue $selectionValue) use ($selectionType, $faker) {
57+
if (config('asseco-custom-fields.migrations.uuid')) {
58+
$selectionValue->id = Str::uuid()->toString();
59+
}
60+
61+
$selectionValue->timestamps = false;
62+
$selectionValue->selection_type_id = $selectionType->id;
63+
$selectionValue->value = $this->fakeValueFromType($selectionType->type->name, $faker);
64+
})->toArray();
7465
}
7566
}

src/Database/Seeders/ValueSeeder.php

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88
use Asseco\CustomFields\App\Contracts\RemoteType;
99
use Asseco\CustomFields\App\Contracts\SelectionType;
1010
use Asseco\CustomFields\App\Contracts\Value;
11+
use Asseco\CustomFields\App\Traits\FakesTypeValues;
1112
use Asseco\CustomFields\App\Traits\FindsTraits;
1213
use Faker\Factory;
13-
use Faker\Generator;
14+
use Illuminate\Database\Eloquent\Model;
1415
use Illuminate\Database\Seeder;
1516
use Illuminate\Support\Str;
1617

1718
class ValueSeeder extends Seeder
1819
{
19-
use FindsTraits;
20+
use FindsTraits, FakesTypeValues;
2021

2122
protected array $cached = [];
2223

@@ -49,7 +50,7 @@ public function run(): void
4950
$model = $models[array_rand($models)];
5051
$selectable = $customField->selectable;
5152
[$type, $typeValue] = $this->getType($selectable);
52-
$fakeValue = $this->fakeValueFromType($type, $typeValue, $faker);
53+
$fakeValue = $typeValue ?: $this->fakeValueFromType($type, $faker);
5354

5455
$value->timestamps = false;
5556
$value->model_type = $model;
@@ -64,6 +65,7 @@ public function run(): void
6465
protected function getCached(string $model): string
6566
{
6667
if (!array_key_exists($model, $this->cached)) {
68+
/** @var Model $model */
6769
$this->cached[$model] = $model::all('id')->pluck('id')->toArray();
6870
}
6971

@@ -72,7 +74,7 @@ protected function getCached(string $model): string
7274
return $cached[array_rand($cached)];
7375
}
7476

75-
protected function getType($selectable)
77+
protected function getType($selectable): array
7678
{
7779
if ($selectable instanceof SelectionType) {
7880
return [$selectable->type->name, $selectable->values->random(1)->first()->value];
@@ -82,26 +84,4 @@ protected function getType($selectable)
8284
return [$selectable->name, null];
8385
}
8486
}
85-
86-
protected function fakeValueFromType($type, $value, Generator $faker)
87-
{
88-
switch ($type) {
89-
case 'integer':
90-
return $value ?: $faker->randomNumber();
91-
case 'float':
92-
return $value ?: $faker->randomFloat();
93-
case 'date':
94-
return $value ?: $faker->date();
95-
case 'time':
96-
return $value ?: $faker->time();
97-
case 'datetime':
98-
return $value ?: $faker->datetime();
99-
case 'text':
100-
return $value ?: $faker->sentence;
101-
case 'boolean':
102-
return $value ?: $faker->boolean;
103-
default:
104-
return $value ?: $faker->word;
105-
}
106-
}
10787
}

tests/TestCase.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Asseco\CustomFields\Tests;
44

5+
use Asseco\Common\CommonServiceProvider;
56
use Asseco\CustomFields\CustomFieldsServiceProvider;
67

78
abstract class TestCase extends \Orchestra\Testbench\TestCase
@@ -15,7 +16,7 @@ public function setUp(): void
1516

1617
protected function getPackageProviders($app)
1718
{
18-
return [CustomFieldsServiceProvider::class];
19+
return [CustomFieldsServiceProvider::class, CommonServiceProvider::class];
1920
}
2021

2122
protected function getEnvironmentSetUp($app)

0 commit comments

Comments
 (0)