Home > other >  Why does my data get changed when I use a primary key other than id?
Why does my data get changed when I use a primary key other than id?

Time:01-09

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 >
    <h1 >Non-driving Reasons</h1>
    <h1 ></h1><!-- spacer beneath title -->

    <div  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 >
            <thead >
                <tr >
                    <th >Non-Driving Reason</th>
                    <th >View</th>
                    <th >Edit</th>
                    <th >Delete</th></tr>
                </thead>
            <tbody >
                @foreach($nonDrivingReasons as $nonDrivingReason)
                <tr >
                    <td >{{$nonDrivingReason->reason_for_not_driving}}</td>
                    <td ><a href="/nonDrivingReasons/show/{{$nonDrivingReason->reason_for_not_driving}}"><img src="{{asset('icons/magenta-details.svg')}}" width="25" alt="view logo"></a></td>
                    <td ><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 ><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 >{{$nonDrivingReasons->links()}}</p>
        </div>

    </div>


    <h1 >&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.

CodePudding user response:

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

  • Related