Skip to content

Commit

Permalink
HasPosition trait
Browse files Browse the repository at this point in the history
  • Loading branch information
netzknecht committed Dec 1, 2023
1 parent abc1db6 commit 04f1996
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,6 @@ public function save()
if (! $this->attribute->id) {
$this->attribute->attribute_type = $this->group->attributable_type;
$this->attribute->attribute_group_id = $this->group->id;
$this->attribute->position = Attribute::whereAttributeGroupId(
$this->group->id
)->count() + 1;
$this->attribute->save();
$this->notify(
__('adminhub::notifications.attribute-edit.created')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,6 @@ public function create()
}

$this->attributeGroup->attributable_type = $this->attributableType;
$this->attributeGroup->position = AttributeGroup::whereAttributableType(
$this->attributableType
)->count() + 1;

$this->attributeGroup->handle = $handle;
$this->attributeGroup->save();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,6 @@ public function save()
return;
}

if (! $this->productOption->position) {
$this->productOption->position = ProductOption::count() + 1;
}

$this->productOption->save();

$this->productOption = new ProductOption();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@ public function save()
$this->validate();

if (! $this->optionValue->id) {
$this->optionValue->position = ProductOptionValue::whereProductOptionId(
$this->option->id
)->count() + 1;

$this->optionValue->option()->associate($this->option);
$this->optionValue->save();
$this->notify(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Lunar\Base\Migration;
use Lunar\Models\Attribute;
use Lunar\Models\AttributeGroup;
use Lunar\Models\ProductOption;
use Lunar\Models\ProductOptionValue;

return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
/**
* Model's with position
*/
$models = [
Attribute::class,
AttributeGroup::class,
ProductOption::class,
ProductOptionValue::class,
];
/**
* Ensure that all position values are unique
*/
foreach ($models as $model) {
$model = app($model);
Schema::table($model->getTable(), function (Blueprint $table) use ($model) {
DB::table($model->getTable())
->select(array_merge(
[$model->getKeyName()],
$model->positionUniqueConstraints()
))
->orderBy('position')
->orderBy('id')
->get()
->groupBy(fn (stdClass $row, int $key) =>
collect($row)
->only($model->positionUniqueConstraints())
->except('position')
->join('-')
)->each
->each(fn(stdClass $row, int $key) => $row->position = $key + 1);
});
}
/**
* Add unique position index under consideration of
* the model's position constraints
*/
foreach ($models as $model) {
$model = app($model);
Schema::table($model->getTable(), function (Blueprint $table) use ($model) {
$schema = Schema::getConnection()
->getDoctrineSchemaManager()
->introspectTable($model->getTable());
$uniqueIndex = $this->prefix . $table->getTable() . '_unique_position';
$uniqueConstraints = array_merge($model->positionUniqueConstraints(), ['position']);
$table->unsignedBigInteger('position')->default(null)->index()->change();
if (!$schema->hasIndex($uniqueIndex)) {
$table->unique($uniqueConstraints, $uniqueIndex);
}
});
}
}
};
87 changes: 87 additions & 0 deletions packages/core/src/Base/Traits/HasPosition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
namespace Lunar\Base\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

trait HasPosition
{

final public static function initializeHasPosition(): void
{
static::saving(function (Model $model) {
if (
$model->isDirty($model->positionUniqueConstraints())
|| !(intval($model->position) > 0)
|| $model->query()
->where($model->getKeyName(), '!=', $model->getKey())
->wherePosition(
$model->position,
$model->getAttributes()
)
->exists()
) {
$model->position = $model->query()
->where($model->getKeyName(), '!=', $model->getKey())
->wherePositionUniqueConstraints(
$model->getAttributes()
)
->max('position') + 1;
}
});
}

final public function positionUniqueConstraints(): array
{
$constraints = ['position'];

if (!property_exists($this, 'positionUniqueConstraints')
|| !is_array($this->positionUniqueConstraints))
{
return $constraints;
}

return array_merge($this->positionUniqueConstraints, $constraints);
}

final public function scopeWherePosition(Builder $query, int $position, array|Collection $constraints = []): void
{
$query
->where('position', $position)
->wherePositionUniqueConstraints($constraints);
}

final public function scopeWherePositionUniqueConstraints(Builder $query, array|Collection $constraints = []): void
{
$constraints = collect($constraints)->except('position');
$modelConstraints = collect($this->positionUniqueConstraints())->reject('position');

if (count($modelConstraints) && !$constraints->hasAny($modelConstraints->toArray())) {
throw new \InvalidArgumentException(
sprintf(
'Position constraints "%s" for "%s" not defined!',
$modelConstraints->diff($constraints)->join('", "', '" and "'),
get_class($this)
)
);
}

$modelConstraints->each(
function ($attribute) use ($query, $constraints) {
if (method_exists($query, Str::camel('scope_' . $attribute))) {
$method = Str::camel($attribute);
} else {
$method = Str::camel('where_' . $attribute);
}
$query->{$method}($constraints[$attribute]);
}
);
}

public function scopePosition(Builder $query, int $position, ...$constraints): void
{
$query->wherePosition($position, $constraints);
}

}
18 changes: 18 additions & 0 deletions packages/core/src/LunarServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -327,5 +327,23 @@ protected function registerBlueprintMacros(): void
);
}
});

Blueprint::macro('position', function (string|array $uniqueConstraints = []) {
/** @var Blueprint $this */
if (is_string($uniqueConstraints)) {
$uniqueConstraints = app($uniqueConstraints)->positionUniqueConstraints();
}
$this->unsignedBigInteger('position')->index();
$this->unique(
array_merge($uniqueConstraints, ['position']),
$this->prefix . $this->table . '_unique_position'

Check failure on line 339 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - L^9.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$prefix.

Check failure on line 339 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - L^9.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$table.

Check failure on line 339 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - L^10.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$prefix.

Check failure on line 339 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - L^10.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$table.

Check failure on line 339 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - L^9.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$prefix.

Check failure on line 339 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - L^9.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$table.

Check failure on line 339 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - L^10.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$prefix.

Check failure on line 339 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - L^10.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$table.
);
});

Blueprint::macro('dropPosition', function () {
/** @var Blueprint $this */
$this->dropUnique($this->prefix . $this->table . '_unique_position');

Check failure on line 345 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - L^9.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$prefix.

Check failure on line 345 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - L^9.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$table.

Check failure on line 345 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - L^10.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$prefix.

Check failure on line 345 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 - L^10.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$table.

Check failure on line 345 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - L^9.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$prefix.

Check failure on line 345 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - L^9.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$table.

Check failure on line 345 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - L^10.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$prefix.

Check failure on line 345 in packages/core/src/LunarServiceProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 - L^10.0 ↑

Access to protected property Illuminate\Database\Schema\Blueprint::$table.
$this->dropColumn('position');
});
}
}
13 changes: 13 additions & 0 deletions packages/core/src/Models/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Lunar\Base\BaseModel;
use Lunar\Base\Traits\HasMacros;
use Lunar\Base\Traits\HasPosition;
use Lunar\Base\Traits\HasTranslations;
use Lunar\Database\Factories\AttributeFactory;
use Lunar\Facades\DB;
Expand Down Expand Up @@ -37,6 +38,7 @@ class Attribute extends BaseModel
{
use HasFactory;
use HasMacros;
use HasPosition;
use HasTranslations;

public static function boot()
Expand Down Expand Up @@ -75,6 +77,17 @@ protected static function newFactory(): AttributeFactory
'configuration' => AsCollection::class,
];

/**
* Define which attributes should be used
* to define the unique position constraint.
*
* @var array
*/
protected $positionUniqueConstraints = [
'attribute_type',
'attribute_group_id',
];

/**
* Return the attribuable relation.
*/
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/Models/AttributeGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Database\Eloquent\Relations\HasMany;
use Lunar\Base\BaseModel;
use Lunar\Base\Traits\HasMacros;
use Lunar\Base\Traits\HasPosition;
use Lunar\Base\Traits\HasTranslations;
use Lunar\Database\Factories\AttributeGroupFactory;

Expand All @@ -23,6 +24,7 @@ class AttributeGroup extends BaseModel
{
use HasFactory;
use HasMacros;
use HasPosition;
use HasTranslations;

/**
Expand Down Expand Up @@ -50,6 +52,16 @@ protected static function newFactory(): AttributeGroupFactory
'name' => AsCollection::class,
];

/**
* Define which attributes should be used
* to define the unique position constraint.
*
* @var array
*/
protected $positionUniqueConstraints = [
'attributable_type',
];

/**
* Return the attributes relationship.
*/
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/Models/ProductOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Lunar\Base\BaseModel;
use Lunar\Base\Traits\HasMacros;
use Lunar\Base\Traits\HasMedia;
use Lunar\Base\Traits\HasPosition;
use Lunar\Base\Traits\HasTranslations;
use Lunar\Base\Traits\Searchable;
use Lunar\Database\Factories\ProductOptionFactory;
Expand All @@ -28,6 +29,7 @@ class ProductOption extends BaseModel implements SpatieHasMedia
use HasFactory;
use HasMacros;
use HasMedia;
use HasPosition;
use HasTranslations;
use Searchable;

Expand All @@ -49,7 +51,7 @@ protected static function newFactory(): ProductOptionFactory
return ProductOptionFactory::new();
}

public function getNameAttribute(string $value): mixed
public function getNameAttribute(?string $value): mixed
{
return json_decode($value);
}
Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/Models/ProductOptionValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Lunar\Base\BaseModel;
use Lunar\Base\Traits\HasMacros;
use Lunar\Base\Traits\HasMedia;
use Lunar\Base\Traits\HasPosition;
use Lunar\Base\Traits\HasTranslations;
use Lunar\Database\Factories\ProductOptionValueFactory;
use Spatie\MediaLibrary\HasMedia as SpatieHasMedia;
Expand All @@ -26,6 +27,7 @@ class ProductOptionValue extends BaseModel implements SpatieHasMedia
use HasFactory;
use HasMacros;
use HasMedia;
use HasPosition;
use HasTranslations;

/**
Expand Down Expand Up @@ -53,7 +55,17 @@ protected static function newFactory(): ProductOptionValueFactory
*/
protected $guarded = [];

public function getNameAttribute(string $value): mixed
/**
* Define which attributes should be used
* to define the unique position constraint.
*
* @var array
*/
protected $positionUniqueConstraints = [
'product_option_id',
];

public function getNameAttribute(?string $value): mixed
{
return json_decode($value);
}
Expand Down

0 comments on commit 04f1996

Please sign in to comment.