SQLSTATE[42S22]: Column not found: 1054 Unknown column 'characters.duel_id' in 'where clause'
I am getting the following error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'characters.duel_id' in 'where clause'
select * from characters where characters.duel_id in (1) and characters.deleted_at is null
Here is my duel model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Duel extends Model
{
use HasFactory;
public function character(): HasMany
{
return $this->hasMany(Character::class);
}
}
Here is the migration:
<?php
use App\Models\Character;
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::create('duels', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(Character::class, "challenger_id")->constrained("characters");
$table->foreignIdFor(Character::class, "opponent_id")->constrained("characters");
$table->tinyInteger("status");
$table->timestamps();
});
// Add the CHECK constraint
DB::statement('ALTER TABLE duels ADD CONSTRAINT status_is_valid_range CHECK (status IN (-4, -3, -2, -1, 0, 1))');
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('duels');
}
};
Here is the duel resource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class DuelResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'challenger_id' => $this->whenLoaded('character', fn () => CharacterResource::make($this->character)),
'opponent_id' => $this->whenLoaded('character', fn () => CharacterResource::make($this->character)),
'status' => $this->status,
];
}
}
Here is my character model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class Character extends Model
{
use HasFactory;
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id',
'active',
'name',
'belt_id',
];
public function user() : BelongsTo
{
return $this->belongsTo(User::class);
}
public function belt(): BelongsTo
{
return $this->belongsTo(Belt::class);
}
}
Here is the character migration:
<?php
use App\Models\Belt;
use App\Models\User;
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::create('characters', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class)->constrained();
$table->boolean("active")->default(false);
$table->string("name", 15);
$table->unsignedInteger("xp")->default(0);
$table->foreignIdFor(Belt::class)->constrained();
$table->unsignedMediumInteger("wins")->default(0);
$table->unsignedMediumInteger("loses")->default(0);
$table->unsignedMediumInteger("draws")->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('characters');
}
};
Here is the character resource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class CharacterResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'xp' => $this->xp,
'belt' => $this->whenLoaded('belt', fn () => BeltResource::make($this->belt)),
'wins' => $this->wins,
'loses' => $this->loses,
'draws' => $this->draws,
'active' => $this->active,
];
}
}
Finally here is the index action of the duel controller:
<?php
namespace App\Http\Controllers;
use App\Http\Resources\DuelResource;
use App\Models\Duel;
use Illuminate\Http\Request;
class DuelController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
// get the character's duels
$duels = Duel::with('character')->
where('challenger_id', auth()->user()->id)
->get();
dd($duels);
return inertia('Duels/Index', [
'duels' => fn() => DuelResource::collection($duels)
]);
}
and the Vue:
<template>
<AppLayout title="My Duels">
<div class="grid row mt-5">
<div class="col-span-6 m-auto">
<div class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<h1 class="text-center mb-3">
<i aria-hidden="true"></i> Duels
</h1>
<div v-if="duels.length === 0">
<p>You have no duels, why not challenge someone?</p>
<Link :href="route('')" class="inline-block border border-blue-500 rounded py-1 px-3 bg-blue-500 text-white" role="link" title="Create a new character">Create a new character</Link>
</div>
<table v-else class="table-auto border-spacing-2 text-center">
<thead>
<tr>
<th scope="col">Opponent</th>
<th scope="col">Options</th>
</tr>
</thead>
<tbody>
<tr v-for="duel in duels" :key="duel.id">
<th scope="row">{{ duel.challenger_id }}</th>
<td>
<Link :href="route('')" class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Cancel</Link>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</AppLayout>
</template>
<script setup>
import AppLayout from "@/Layouts/AppLayout.vue";
import {Link} from "@inertiajs/vue3";
defineProps(['duels']);
</script>
2 Characters fight each other in a single duel. However a character can participate in many duels at once. Here is a video of the now defunt game with 2 player characters fighting each other which should explain the concept of a duel. I tried asking my friend Larry QuickDraw which suggested pivot tables
. When I tried to prototype this in Django I didn't need to use pivot tables
so the concept of pivot tables is forign to me. Any ideas?
Hi,
Based on the error message and the code you've provided, it seems the main issue is that there's a mismatch between your database schema and your model relationships.
The error "Unknown column 'characters.duel_id' in 'where clause'" suggests that your query is trying to access a 'duel_id' column in the 'characters' table, which doesn't exist.
From your migrations, it's clear that the 'duels' table has foreign keys to the 'characters' table (challenger_id and opponent_id), not the other way around. This means a Duel belongs to two Characters, not that a Character has many Duels.
Your current setup in the Duel model is incorrect. Instead of hasMany, you should use belongsTo for both challenger and opponent.
Here's how you can fix this:
- Update the Duel model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Duel extends Model
{
use HasFactory;
public function challenger(): BelongsTo
{
return $this->belongsTo(Character::class, 'challenger_id');
}
public function opponent(): BelongsTo
{
return $this->belongsTo(Character::class, 'opponent_id');
}
}
- Update the Character model to add the inverse relationships:
public function challengerDuels(): HasMany
{
return $this->hasMany(Duel::class, 'challenger_id');
}
public function opponentDuels(): HasMany
{
return $this->hasMany(Duel::class, 'opponent_id');
}
- Update your DuelController index method:
public function index()
{
$userId = auth()->id();
$duels = Duel::with(['challenger', 'opponent'])
->where('challenger_id', $userId)
->orWhere('opponent_id', $userId)
->get();
return inertia('Duels/Index', [
'duels' => DuelResource::collection($duels)
]);
}
- Update your DuelResource:
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'challenger' => CharacterResource::make($this->whenLoaded('challenger')),
'opponent' => CharacterResource::make($this->whenLoaded('opponent')),
'status' => $this->status,
];
}
- In your Vue component, update how you're displaying the duel information:
<tr v-for="duel in duels" :key="duel.id">
<th scope="row">{{ duel.opponent.name }}</th>
<td>
<Link :href="route('duel.cancel', duel.id)" class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Cancel</Link>
</td>
</tr>
You don't need pivot tables for this scenario because the relationship is directly established through foreign keys in the duels table.
- Bobby