英文:
Why does my data get changed when I use a primary key other than id?
问题
我正在开发一个使用Laravel 9.x的应用程序,我的应用程序目前包含7个表。每个表都有一个主键,没有一个主键是标准的自增整数id
。对于我的每个模型,我已经在模型中添加了一行,指示实际的主键是什么,并指示不递增。我的迁移也正确定义了它们各自的主键。
我正在为我的7个控制器编写index()和show()方法时遇到了一个非常奇怪的问题:当我执行index()方法时,主键列中的值会从我在DatabaseSeeder中种子的值更改。在一个表中,主键是一个字符串,index()方法在表的每一行的主键列中显示为0。在另一个表中,主键是一个日期,index()方法在表的每一行的主键列中只显示年份部分。
我发现唯一能够在主键列中显示正确值的方法是注释掉模型中的主键定义。
我对这种行为感到非常困惑。有人能够解释为什么会发生这种情况以及如何修复吗?
这是我迁移中定义一个表的up()方法的代码示例:
public function up()
{
Schema::create('non_driving_reasons', function (Blueprint $table) {
$table->string('reason_for_not_driving', 50);
$table->primary('reason_for_not_driving', 'reason_for_not_driving_PK');
$table->timestamps();
});
}
这是向该表中种子的值的示例:
\App\Models\NonDrivingReasons::factory()->create([
'reason_for_not_driving' => 'Snow Day'
]);
\App\Models\NonDrivingReasons::factory()->create([
'reason_for_not_driving' => 'Professional Development Day'
]);
\App\Models\NonDrivingReasons::factory()->create([
'reason_for_not_driving' => 'School staff on strike'
]);
\App\Models\NonDrivingReasons::factory()->create([
'reason_for_not_driving' => 'School Holiday - Christmas'
]);
这是NonDrivingReasonsController的index()方法:
public function index() {
return view('nonDrivingReasons.index', [
'nonDrivingReasons' => NonDrivingReasons::paginate(6)
]);
}
这是NonDrivingReasons表的index视图:
<x-layout>
<div class="bg-gradient-to-b from-red-500 to-purple-100">
<h1 class="text-3xl text-center text-white font-bold bg-indigo-700">Non-driving Reasons</h1>
<h1 class="pb-5"></h1><!-- spacer beneath title -->
<div class="flex justify-center h-screen" style="display: grid; grid-template-rows: auto auto auto auto; row-gap: 25px; padding: 10px">
<div>
{{-- If there are no non-driving reasons, indicate that to the user. --}}
@if (count($nonDrivingReasons) == 0)
<p>No non-driving reasons found.</p>
@endif
</div>
<div>
<table><thead><tr><th><a href="/nonDrivingReasons/create"><img src="{{asset('icons/green-plus-circle-outline.svg')}}" width="50" alt="add icon"></a></th><th><a href="/nonDrivingReasons/create">Add new non-driving reason</a></th></tr></thead></table>
</div>
{{-- If there are non-driving reasons, display them. --}}
<div>
<table class="border-black border-2 bg-white">
<thead class="border-black border-2 text-white bg-gray-800">
<tr class="content-center">
<th class="border-black border-2 p-2 text-left">Non-Driving Reason</th>
<th class="border-black border-2 p-2">View</th>
<th class="border-black border-2 p-2">Edit</th>
<th class="border-black border-2 p-2">Delete</th></tr>
</thead>
<tbody class="border-black border-2">
@foreach($nonDrivingReasons as $nonDrivingReason)
<tr class="border-black border-2">
<td class="border-black border-2 p-2">{{$nonDrivingReason->reason_for_not_driving}}</td>
<td class="border-black border-2 p-2"><a href="/nonDrivingReasons/show/{{$nonDrivingReason->reason_for_not_driving}}"><img src="{{asset('icons/magenta-details.svg')}}" width="25" alt="view logo"></a></td>
<td class="border-black border-2 p-2"><a href="/nonDrivingReasons/edit/{{$nonDrivingReason->reason_for_not_driving}}"><img src="{{asset('icons/yellow-lead-pencil.svg')}}" width="25" alt="update logo"></a></td>
<td class="border-black border-2 p-2"><a href="/nonDrivingReasons/destroy/{{$nonDrivingReason->reason_for_not_driving}}"><img src="{{asset('icons/red-delete.svg')}}" width="25" alt="delete logo"></a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div>
<p class="mt-6 p-4">{{$nonDrivingReasons->links()}}</p>
</div>
</div>
<h1 class="pt-5"> </h1><!-- spacer at bottom of page -->
</div>
</x-layout>
这是NonDrivingReasons模型:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class NonDrivingReasons extends Model
{
use HasFactory;
protected $fillable = ['reason_for_not_driving'];
protected $primaryKey = 'reason_for_not_driving';
public $incrementing = 'false';
/**
* Set the keys for a save update query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function setKeysForSaveQuery($query)
{
$keys = $this->getKeyName();
if(!is_array($keys)){
return parent::setKeysForSaveQuery($query);
}
foreach($keys as $keyName){
$query->where($keyName, '=', $this->getKeyForSaveQuery($keyName));
}
return $query;
}
/**
* Get the primary key value for a save query.
*
* @param mixed $keyName
* @return mixed
*/
protected function getKeyForSaveQuery($keyName = null)
{
if(is_null($keyName)){
$keyName = $this->getKeyName();
}
if (isset($this->original[$keyName])) {
return $this->original[$keyName];
}
return $this->getAttribute($keyName);
}
}
再次强调,为了使表中的非驾驶原因显示正确的数据,我只需要在模型中注释掉$primaryKey = 'reason_for_not_driving';
这一行。但这不是一个好的解决方案,因为它会导致控制器中的表查询失败,因为Eloquent假设主键必须是id,而在表中不存在id。
英文:
I am developing an app in Laravel 9.x and my app currently includes 7 tables. Each table has a primary key and NONE of those primary keys is the standard incrementing integer id
. For each of my models, I have added a line to my model indicating what the actual primary key is and indicate that the incrementing is false. My migrations also define their respective primary key correctly.
I'm currently writing the index() and show() methods for my 7 Controllers. Something very strange happens when I execute the index() method: the value in the primary key column is changed from the value that I seeded in my DatabaseSeeder. In one table, where the primary key is a string, the index() method is displaying a 0 in the primary key column of every single row of the table. In another table, where the primary key is a date, the index() method is displaying only the year portion of the date in the primary key column of every row of the table.
The only way I've found that will cause the correct value to display in the primary key column is to comment out the primary key definition in the model.
I'm absolutely baffled by this behaviour. Can anyone suggest why this is happening and how to fix it?
This is the up() method of my migration where one of the tables gets defined:
public function up()
{
Schema::create('non_driving_reasons', function (Blueprint $table) {
$table->string('reason_for_not_driving', 50);
$table->primary('reason_for_not_driving', 'reason_for_not_driving_PK');
$table->timestamps();
});
}
This is a sample of the values being seeded into this table:
\App\Models\NonDrivingReasons::factory()->create([
'reason_for_not_driving' => 'Snow Day'
]);
\App\Models\NonDrivingReasons::factory()->create([
'reason_for_not_driving' => 'Professional Development Day'
]);
\App\Models\NonDrivingReasons::factory()->create([
'reason_for_not_driving' => 'School staff on strike'
]);
\App\Models\NonDrivingReasons::factory()->create([
'reason_for_not_driving' => 'School Holiday - Christmas'
]);
This is the index() method of the NonDrivingReasonsController:
public function index() {
return view('nonDrivingReasons.index', [
'nonDrivingReasons' => NonDrivingReasons::paginate(6)
]);
}
This is the index blade for the NonDrivingReasons table:
<x-layout>
<div class="bg-gradient-to-b from-red-500 to-purple-100">
<h1 class="text-3xl text-center text-white font-bold bg-indigo-700">Non-driving Reasons</h1>
<h1 class="pb-5"></h1><!-- spacer beneath title -->
<div class="flex justify-center h-screen" style="display: grid; grid-template-rows: auto auto auto auto; row-gap: 25px; padding: 10px">
<div>
{{-- If there are no non-driving reasons, indicate that to the user. --}}
@if (count($nonDrivingReasons) == 0)
<p>No non-driving reasons found.</p>
@endif
</div>
<div>
<table><thead><tr><th><a href="/nonDrivingReasons/create"><img src="{{asset('icons/green-plus-circle-outline.svg')}}" width="50" alt="add icon"></a></th><th><a href="/nonDrivingReasons/create">Add new non-driving reason</a></th></tr></thead></table>
</div>
{{-- If there are non-driving reasons, display them. --}}
<div>
<table class="border-black border-2 bg-white">
<thead class="border-black border-2 text-white bg-gray-800">
<tr class="content-center">
<th class="border-black border-2 p-2 text-left">Non-Driving Reason</th>
<th class="border-black border-2 p-2">View</th>
<th class="border-black border-2 p-2">Edit</th>
<th class="border-black border-2 p-2">Delete</th></tr>
</thead>
<tbody class="border-black border-2">
@foreach($nonDrivingReasons as $nonDrivingReason)
<tr class="border-black border-2">
<td class="border-black border-2 p-2">{{$nonDrivingReason->reason_for_not_driving}}</td>
<td class="border-black border-2 p-2"><a href="/nonDrivingReasons/show/{{$nonDrivingReason->reason_for_not_driving}}"><img src="{{asset('icons/magenta-details.svg')}}" width="25" alt="view logo"></a></td>
<td class="border-black border-2 p-2"><a href="/nonDrivingReasons/edit/{{$nonDrivingReason->reason_for_not_driving}}"><img src="{{asset('icons/yellow-lead-pencil.svg')}}" width="25" alt="update logo"></a></td>
<td class="border-black border-2 p-2"><a href="/nonDrivingReasons/destroy/{{$nonDrivingReason->reason_for_not_driving}}"><img src="{{asset('icons/red-delete.svg')}}" width="25" alt="delete logo"></a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div>
<p class="mt-6 p-4">{{$nonDrivingReasons->links()}}</p>
</div>
</div>
<h1 class="pt-5">&nbsp;</h1><!-- spacer at bottom of page -->
</div>
</x-layout>
And this is the model for NonDrivingReasons:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class NonDrivingReasons extends Model
{
use HasFactory;
protected $fillable = ['reason_for_not_driving'];
protected $primaryKey = 'reason_for_not_driving';
public $incrementing = 'false';
/**
* Set the keys for a save update query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function setKeysForSaveQuery($query)
{
$keys = $this->getKeyName();
if(!is_array($keys)){
return parent::setKeysForSaveQuery($query);
}
foreach($keys as $keyName){
$query->where($keyName, '=', $this->getKeyForSaveQuery($keyName));
}
return $query;
}
/**
* Get the primary key value for a save query.
*
* @param mixed $keyName
* @return mixed
*/
protected function getKeyForSaveQuery($keyName = null)
{
if(is_null($keyName)){
$keyName = $this->getKeyName();
}
if (isset($this->original[$keyName])) {
return $this->original[$keyName];
}
return $this->getAttribute($keyName);
}
}
Again, all I need to do to make the correct data appear in the Non-Driving Reason of the table is comment out $primaryKey = 'reason_for_not_driving';
in my model. But that is not a good solution because it causes the query of the table to fail in the Controller because Eloquent assumes the primary key must be id, which is not present in the table.
答案1
得分: 3
如果定义的主键不是integer
类型,你需要相应地定义$keyType
。
protected $keyType = 'string';
关于主键的更多信息可以在文档中阅读:
https://laravel.com/docs/9.x/eloquent#primary-keys
英文:
If the defined primary key is not of type integer
, you will have to define the $keyTpye
accordingly.
protected $keyType = 'string';
More about primary keys can be read in the docs:
https://laravel.com/docs/9.x/eloquent#primary-keys
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论