Skip to content

Commit

Permalink
feat(features): make features able to have multiple subtypes
Browse files Browse the repository at this point in the history
  • Loading branch information
ScuffedNewt committed Oct 8, 2024
1 parent 8128485 commit 9c805df
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 60 deletions.
24 changes: 13 additions & 11 deletions app/Http/Controllers/Admin/Data/FeatureController.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public function postSortFeatureCategory(Request $request, FeatureService $servic
*/
public function getFeatureIndex(Request $request) {
$query = Feature::query();
$data = $request->only(['rarity_id', 'feature_category_id', 'species_id', 'subtype_id', 'name', 'sort', 'visibility']);
$data = $request->only(['rarity_id', 'feature_category_id', 'species_id', 'subtype_ids', 'name', 'sort', 'visibility']);
if (isset($data['rarity_id']) && $data['rarity_id'] != 'none') {
$query->where('rarity_id', $data['rarity_id']);
}
Expand All @@ -182,11 +182,13 @@ public function getFeatureIndex(Request $request) {
$query->where('species_id', $data['species_id']);
}
}
if (isset($data['subtype_id']) && $data['subtype_id'] != 'none') {
if ($data['subtype_id'] == 'withoutOption') {
$query->whereNull('subtype_id');
if (isset($data['subtype_ids']) && $data['subtype_ids']) {
if (!in_array('withoutOption', $data['subtype_ids'])) {
$query->whereJsonContains('subtype_ids', $data['subtype_ids']);
} else {
$query->where('subtype_id', $data['subtype_id']);
$query->where(function ($query) use ($data) {
$query->whereNull('subtype_ids')->orWhereJsonLength('subtype_ids', 0);
});
}
}
if (isset($data['name'])) {
Expand Down Expand Up @@ -238,7 +240,7 @@ public function getFeatureIndex(Request $request) {
'features' => $query->paginate(20)->appends($request->query()),
'rarities' => ['none' => 'Any Rarity'] + Rarity::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'specieses' => ['none' => 'Any Species'] + ['withoutOption' => 'Without Species'] + Species::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'subtypes' => ['none' => 'Any Subtype'] + ['withoutOption' => 'Without Subtype'] + Subtype::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'subtypes' => ['withoutOption' => 'Without Subtype'] + Subtype::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'categories' => ['none' => 'Any Category'] + ['withoutOption' => 'Without Category'] + FeatureCategory::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
]);
}
Expand Down Expand Up @@ -275,7 +277,7 @@ public function getEditFeature($id) {
'feature' => $feature,
'rarities' => ['none' => 'Select a Rarity'] + Rarity::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'specieses' => ['none' => 'No restriction'] + Species::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'subtypes' => ['none' => 'No subtype'] + Subtype::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'subtypes' => Subtype::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'categories' => ['none' => 'No category'] + FeatureCategory::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
]);
}
Expand All @@ -291,7 +293,7 @@ public function getEditFeature($id) {
public function postCreateEditFeature(Request $request, FeatureService $service, $id = null) {
$id ? $request->validate(Feature::$updateRules) : $request->validate(Feature::$createRules);
$data = $request->only([
'name', 'species_id', 'subtype_id', 'rarity_id', 'feature_category_id', 'description', 'image', 'remove_image', 'is_visible',
'name', 'species_id', 'subtype_ids', 'rarity_id', 'feature_category_id', 'description', 'image', 'remove_image', 'is_visible',
]);
if ($id && $service->updateFeature(Feature::find($id), $data, Auth::user())) {
flash('Trait updated successfully.')->success();
Expand Down Expand Up @@ -350,11 +352,11 @@ public function postDeleteFeature(Request $request, FeatureService $service, $id
*/
public function getCreateEditFeatureSubtype(Request $request) {
$species = $request->input('species');
$subtype_id = $request->input('subtype_id');
$subtype_ids = $request->input('subtype_ids');

return view('admin.features._create_edit_feature_subtype', [
'subtypes' => ['0' => 'Select Subtype'] + Subtype::where('species_id', '=', $species)->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'subtype_id' => $subtype_id,
'subtypes' => Subtype::where('species_id', '=', $species)->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'subtype_ids' => $subtype_ids,
]);
}
}
14 changes: 8 additions & 6 deletions app/Http/Controllers/WorldController.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public function getFeatureCategories(Request $request) {
*/
public function getFeatures(Request $request) {
$query = Feature::visible(Auth::user() ?? null)->with('category')->with('rarity')->with('species');
$data = $request->only(['rarity_id', 'feature_category_id', 'species_id', 'subtype_id', 'name', 'sort']);
$data = $request->only(['rarity_id', 'feature_category_id', 'species_id', 'subtype_ids', 'name', 'sort']);
if (isset($data['rarity_id']) && $data['rarity_id'] != 'none') {
$query->where('rarity_id', $data['rarity_id']);
}
Expand All @@ -166,11 +166,13 @@ public function getFeatures(Request $request) {
$query->where('species_id', $data['species_id']);
}
}
if (isset($data['subtype_id']) && $data['subtype_id'] != 'none') {
if ($data['subtype_id'] == 'withoutOption') {
$query->whereNull('subtype_id');
if (isset($data['subtype_ids']) && $data['subtype_ids']) {
if (!in_array('withoutOption', $data['subtype_ids'])) {
$query->whereJsonContains('subtype_ids', $data['subtype_ids']);
} else {
$query->where('subtype_id', $data['subtype_id']);
$query->where(function ($query) use ($data) {
$query->whereNull('subtype_ids')->orWhereJsonLength('subtype_ids', 0);
});
}
}
if (isset($data['name'])) {
Expand Down Expand Up @@ -215,7 +217,7 @@ public function getFeatures(Request $request) {
'features' => $query->orderBy('id')->paginate(20)->appends($request->query()),
'rarities' => ['none' => 'Any Rarity'] + Rarity::orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'specieses' => ['none' => 'Any Species'] + ['withoutOption' => 'Without Species'] + Species::visible(Auth::user() ?? null)->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'subtypes' => ['none' => 'Any Subtype'] + ['withoutOption' => 'Without Subtype'] + Subtype::visible(Auth::user() ?? null)->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'subtypes' => ['withoutOption' => 'Without Subtype'] + Subtype::visible(Auth::user() ?? null)->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
'categories' => ['none' => 'Any Category'] + ['withoutOption' => 'Without Category'] + FeatureCategory::visible(Auth::user() ?? null)->orderBy('sort', 'DESC')->pluck('name', 'id')->toArray(),
]);
}
Expand Down
37 changes: 26 additions & 11 deletions app/Models/Feature/Feature.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Feature extends Model {
* @var array
*/
protected $fillable = [
'feature_category_id', 'species_id', 'subtype_id', 'rarity_id', 'name', 'has_image', 'description', 'parsed_description', 'is_visible', 'hash',
'feature_category_id', 'species_id', 'subtype_ids', 'rarity_id', 'name', 'has_image', 'description', 'parsed_description', 'is_visible', 'hash',
];

/**
Expand All @@ -24,6 +24,16 @@ class Feature extends Model {
* @var string
*/
protected $table = 'features';

/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'subtype_ids' => 'array',
];

/**
* Validation rules for creation.
*
Expand All @@ -32,7 +42,7 @@ class Feature extends Model {
public static $createRules = [
'feature_category_id' => 'nullable',
'species_id' => 'nullable',
'subtype_id' => 'nullable',
'subtype_ids' => 'nullable',
'rarity_id' => 'required|exists:rarities,id',
'name' => 'required|unique:features|between:3,100',
'description' => 'nullable',
Expand All @@ -47,7 +57,7 @@ class Feature extends Model {
public static $updateRules = [
'feature_category_id' => 'nullable',
'species_id' => 'nullable',
'subtype_id' => 'nullable',
'subtype_ids' => 'nullable',
'rarity_id' => 'required|exists:rarities,id',
'name' => 'required|between:3,100',
'description' => 'nullable',
Expand All @@ -74,13 +84,6 @@ public function species() {
return $this->belongsTo(Species::class);
}

/**
* Get the subtype the feature belongs to.
*/
public function subtype() {
return $this->belongsTo(Subtype::class);
}

/**
* Get the category the feature belongs to.
*/
Expand Down Expand Up @@ -144,7 +147,7 @@ public function scopeSortSpecies($query) {
public function scopeSortSubtype($query) {
$ids = Subtype::orderBy('sort', 'DESC')->pluck('id')->toArray();

return count($ids) ? $query->orderBy(DB::raw('FIELD(subtype_id, '.implode(',', $ids).')')) : $query;
return count($ids) ? $query->orderBy(DB::raw('FIELD(subtype_ids, '.implode(',', $ids).')')) : $query;
}

/**
Expand Down Expand Up @@ -331,4 +334,16 @@ public static function getDropdownItems($withHidden = 0) {
return self::where('is_visible', '>=', $visibleOnly)->orderBy('name')->pluck('name', 'id')->toArray();
}
}

/**
* Returns the subtype display for the feature.
*/
public function displaySubtypes() {
$result = [];
foreach ($this->subtype_ids as $id) {
$result[] = Subtype::find($id)->displayName;
}

return implode(', ', $result);
}
}
41 changes: 23 additions & 18 deletions app/Services/FeatureService.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ public function createFeature($data, $user) {
if (isset($data['species_id']) && $data['species_id'] == 'none') {
$data['species_id'] = null;
}
if (isset($data['subtype_id']) && $data['subtype_id'] == 'none') {
$data['subtype_id'] = null;
if (!isset($data['subtype_ids']) || !$data['subtype_ids']) {
$data['subtype_ids'] = null;
}

if ((isset($data['feature_category_id']) && $data['feature_category_id']) && !FeatureCategory::where('id', $data['feature_category_id'])->exists()) {
Expand All @@ -210,13 +210,15 @@ public function createFeature($data, $user) {
if ((isset($data['species_id']) && $data['species_id']) && !Species::where('id', $data['species_id'])->exists()) {
throw new \Exception('The selected species is invalid.');
}
if (isset($data['subtype_id']) && $data['subtype_id']) {
$subtype = Subtype::find($data['subtype_id']);
if (!(isset($data['species_id']) && $data['species_id'])) {
throw new \Exception('Species must be selected to select a subtype.');
}
if (!$subtype || $subtype->species_id != $data['species_id']) {
throw new \Exception('Selected subtype invalid or does not match species.');
if (isset($data['subtype_ids']) && $data['subtype_ids']) {
foreach ($data['subtype_ids'] as $subtype_id) {
$subtype = Subtype::find($data['subtype_id']);
if (!(isset($data['species_id']) && $data['species_id'])) {
throw new \Exception('Species must be selected to select a subtype.');
}
if (!$subtype || $subtype->species_id != $data['species_id']) {
throw new \Exception('Selected subtype invalid or does not match species.');
}
}
}

Expand Down Expand Up @@ -269,8 +271,8 @@ public function updateFeature($feature, $data, $user) {
if (isset($data['species_id']) && $data['species_id'] == 'none') {
$data['species_id'] = null;
}
if (isset($data['subtype_id']) && $data['subtype_id'] == 'none') {
$data['subtype_id'] = null;
if (!isset($data['subtype_ids']) || !$data['subtype_ids']) {
$data['subtype_ids'] = null;
}

// More specific validation
Expand All @@ -283,13 +285,16 @@ public function updateFeature($feature, $data, $user) {
if ((isset($data['species_id']) && $data['species_id']) && !Species::where('id', $data['species_id'])->exists()) {
throw new \Exception('The selected species is invalid.');
}
if (isset($data['subtype_id']) && $data['subtype_id']) {
$subtype = Subtype::find($data['subtype_id']);
if (!(isset($data['species_id']) && $data['species_id'])) {
throw new \Exception('Species must be selected to select a subtype.');
}
if (!$subtype || $subtype->species_id != $data['species_id']) {
throw new \Exception('Selected subtype invalid or does not match species.');

if (isset($data['subtype_ids']) && $data['subtype_ids']) {
foreach ($data['subtype_ids'] as $subtype_id) {
$subtype = Subtype::find($subtype_id);
if (!(isset($data['species_id']) && $data['species_id'])) {
throw new \Exception('Species must be selected to select a subtype.');
}
if (!$subtype || $subtype->species_id != $data['species_id']) {
throw new \Exception('Selected subtype invalid or does not match species.');
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
//
Schema::table('features', function (Blueprint $table) {
$table->dropColumn('subtype_id');
$table->json('subtype_ids')->nullable()->default(null);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
//
Schema::table('features', function (Blueprint $table) {
$table->dropColumn('subtype_ids');
$table->integer('subtype_id')->nullable()->default(null);
});
}
};
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
{!! Form::label('Subtype (Optional)') !!} {!! add_help('This is cosmetic and does not limit choice of traits in selections.') !!}
{!! Form::select('subtype_id', $subtypes, $subtype_id, ['class' => 'form-control', 'id' => 'subtype']) !!}
{!! Form::label('Subtypes (Optional)') !!} {!! add_help('This is cosmetic and does not limit choice of traits in selections.') !!}
{!! Form::select('subtype_ids[]', $subtypes, $subtype_ids, ['class' => 'form-control selectize', 'id' => 'subtype', 'multiple', 'placeholder' => 'Select Subtypes']) !!}
11 changes: 7 additions & 4 deletions resources/views/admin/features/create_edit_feature.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
{!! Form::select('species_id', $specieses, $feature->species_id, ['class' => 'form-control', 'id' => 'species']) !!}
</div>
<div class="col-md-4 form-group" id="subtypes">
{!! Form::label('Subtype (Optional)') !!} {!! add_help('This is cosmetic and does not limit choice of traits in selections.') !!}
{!! Form::select('subtype_id', $subtypes, $feature->subtype_id, ['class' => 'form-control', 'id' => 'subtype']) !!}
{!! Form::label('Subtypes (Optional)') !!} {!! add_help('This is cosmetic and does not limit choice of traits in selections.') !!}
{!! Form::select('subtype_ids[]', $subtypes, $feature->subtype_ids, ['class' => 'form-control selectize', 'id' => 'subtype', 'multiple', 'placeholder' => 'Select a Species']) !!}
</div>
</div>
<div class="form-group">
Expand Down Expand Up @@ -84,6 +84,8 @@
@parent
<script>
$(document).ready(function() {
$('.selectize').selectize();
$('.delete-feature-button').on('click', function(e) {
e.preventDefault();
loadModal("{{ url('admin/data/traits/delete') }}/{{ $feature->id }}", 'Delete Trait');
Expand All @@ -97,13 +99,14 @@
function refreshSubtype() {
var species = $('#species').val();
var subtype_id = {{ $feature->subtype_id ?: 'null' }};
var subtype_ids = {{ json_encode($feature->subtype_ids) ?: 'null' }};
$.ajax({
type: "GET",
url: "{{ url('admin/data/traits/check-subtype') }}?species=" + species + "&subtype_id=" + subtype_id,
url: "{{ url('admin/data/traits/check-subtype') }}?species=" + species + "&subtype_ids=" + subtype_ids,
dataType: "text"
}).done(function(res) {
$("#subtypes").html(res);
$('.selectize').selectize();
}).fail(function(jqXHR, textStatus, errorThrown) {
alert("AJAX call failed: " + textStatus + ", " + errorThrown);
});
Expand Down
13 changes: 9 additions & 4 deletions resources/views/admin/features/features.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
<div class="form-group ml-3 mb-3">
{!! Form::select('species_id', $specieses, Request::get('species_id'), ['class' => 'form-control']) !!}
</div>
<div class="form-group ml-3 mb-3">
{!! Form::select('subtype_id', $subtypes, Request::get('subtype_id'), ['class' => 'form-control']) !!}
<div class="form-group ml-3 mb-3 mt-2">
{!! Form::select('subtype_ids[]', $subtypes, Request::get('subtype_ids'), ['class' => 'form-control selectize', 'multiple', 'placeholder' => 'Any Subtype']) !!}
</div>
<div class="form-group ml-3 mb-3">
{!! Form::select('rarity_id', $rarities, Request::get('rarity_id'), ['class' => 'form-control']) !!}
Expand Down Expand Up @@ -93,7 +93,7 @@
<div class="logs-table-cell">Species</div>
</div>
<div class="col-6 col-md-2">
<div class="logs-table-cell">Subtype</div>
<div class="logs-table-cell">Subtypes</div>
</div>
</div>
</div>
Expand All @@ -119,7 +119,7 @@
<div class="logs-table-cell">{{ $feature->species ? $feature->species->name : '---' }}</div>
</div>
<div class="col-6 col-md-2">
<div class="logs-table-cell">{{ $feature->subtype ? $feature->subtype->name : '---' }}</div>
<div class="logs-table-cell">{!! $feature->subtype_ids ? $feature->displaySubtypes() : '---' !!}</div>
</div>
<div class="col-12 col-md-1">
<div class="logs-table-cell"><a href="{{ url('admin/data/traits/edit/' . $feature->id) }}" class="btn btn-primary py-0 px-1 w-100">Edit</a></div>
Expand All @@ -137,4 +137,9 @@

@section('scripts')
@parent
<script>
$(document).ready(function() {
$('.selectize').selectize();
})
</script>
@endsection
4 changes: 2 additions & 2 deletions resources/views/world/_feature_entry.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
@if ($feature->species_id)
<div>
<strong>Species:</strong> {!! $feature->species->displayName !!}
@if ($feature->subtype_id)
({!! $feature->subtype->displayName !!} subtype)
@if ($feature->subtype_ids)
({!! $feature->displaySubtypes() !!} subtype{{ count($feature->subtype_ids) > 1 ? 's' : '' }})
@endif
</div>
@endif
Expand Down
Loading

0 comments on commit 9c805df

Please sign in to comment.