简体   繁体   中英

How to get last inserted non incremental id using eloquent in Laravel?

I have two models Customer and Address . My Customer has non incremental primary key and is of type string which is customer_id . The relationship between these two models is of one to many, that means for single customer many addresses for example: invoice address, delivery address, current address, etc. My Customer model is as shown below:

Customer.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Customer extends Model
{
    protected $keyType = 'string';
    protected $primaryKey = 'customer_id';
    public $incrementing = false;

    public function addresses()
    {
        return $this->hasMany('App\Address','customer_id');
    }
}

And my Address model is as shown below:

Address.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Address extends Model
{
    //
    public $timestamps = false;
    // protected $table = "addresses";

    public function customer()
    {
        return $this->belongsTo('App\Customer');
    }
}

And following shows the migration for my customers table

migration for customers table

<?php

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

class CreateCustomersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('customers', function (Blueprint $table) {
            $table->string('customer_id');
            $table->string('name');
            $table->string('type');
            $table->date('dob');
            $table->type('country_code');

            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('customers');
    }
}

The other thing to note is that my customer_id is incremental in sense that I have created separate table namely customer_sequence which is auto incremental and before inserting record I append it with two character code using trigger and then place it into my customers table. My customer_sequence migration is as shown below

customer_sequence Migration

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

class CreateSequenceCustomers extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('sequence_customers', function (Blueprint $table) {
            $table->increments('id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('sequence_customers');
    }
}

And the trigger that I have used for inserting incrementing string id is as follows:

Migration for customer_id trigger

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

class CreateTriggerCustomers extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        DB::unprepared("
        CREATE TRIGGER tg_customer_insert
            BEFORE INSERT ON customers
            FOR EACH ROW
            BEGIN
                INSERT INTO sequence_customers(id) VALUES (NULL);
                IF NEW.type ='Private' THEN
                    SET NEW.customer_id = CONCAT(NEW.country_code, LPAD(LAST_INSERT_ID(), 5, '0'));
                ELSEIF NEW.type='Business' THEN
                    SET NEW.customer_id = CONCAT(NEW.country_code, LPAD(LAST_INSERT_ID(), 5, '0'));
                ELSEIF NEW.type='Reseller' THEN
                    SET NEW.customer_id = LPAD(LAST_INSERT_ID(), 5, '0');
                ELSEIF NEW.type='Distributor' THEN
                    SET NEW.customer_id = LPAD(LAST_INSERT_ID(), 5, '0');
                ELSEIF NEW.type='Other' THEN
                    SET NEW.customer_id = LPAD(LAST_INSERT_ID(), 5, '0');
                END IF;
                IF NEW.credit_amount > NEW.credit_limit THEN
                   SET NEW.credit_limit_exceeded=TRUE;
                ELSE
                    SET NEW.credit_limit_exceeded=FALSE;
                END IF;
            END
        ");
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        DB::unprepared('DROP TRIGGER IF EXISTS tg_customer_insert');
    }
}

Now when I save the data of customer and try to get id from customer model it returns me null . My controller is as shown below:

CustomerController.php

public function store(Request $request)
{
    $customer = new Customer;
    $invoiceAddress = new Address;
    $deliveryAddress = new Address;

    $customer->name = $request->name;
    $customer->type = $request->type;
    $customer->dob = $request->dob;
    $customer->country_code=$request->country_code;
    $customer->save();

    $deliveryAddress->street_name_no = $request->street_name_no;
    $deliveryAddress->city = $request->city;
    $deliveryAddress->country = $request->country;

    //This throws error customer_id cannot be null integrity constraint
    $deliveryAddress->customer_id = $customer->customer_id;
    $deliveryAddress->save();
}

This is because you are assigning request values to your customer variable.

$customer=new Customer;

$customer=$request->name;
$customer=$request->type;
$customer=$request->dob;
$customer->save();

When you are calling save() , you are actually calling save() on a string. Fix it by specifying the fillable properties on your Customer model. This is just an example.

$customer = new Customer();

$customer->name = $request->name;
$customer->type = $request->type;
$customer->dob  = $request->dob;
$customer->save();

After that, $customer->customer_id should not be null.

Edit: Failed to notice the following line:

public $incrementing = false;

which means at the time of creating your Customer you would also have to supply the customer_id , since it is no longer auto-incrementing.

I also took a deeper look at the API. It seems Laravel won't be aware of the attribute set by the trigger at that stage. You can try to refresh() the model which will pull in fresh attributes from the DB and assuming your triggers are working fine, you should be getting back a customer_id .

So essentially, just add this line before adding the Delivery Address.

$customer->refresh();

I also noticed you don't have any logic to redirect the user back on successful save. I suspect this is why it is throwing the 404 since the same route isn't defined for a GET request.

public function store(Request $request)
{
    $customer        = new Customer;
    $invoiceAddress  = new Address;
    $deliveryAddress = new Address;

    $customer->name = $request->name;
    $customer->type = $request->type;
    $customer->dob  = $request->dob;
    $customer->country_code = $request->country_code;

    $customer->save();

    $customer->refresh(); 

    $deliveryAddress->street_name_no = $request->street_name_no;
    $deliveryAddress->city = $request->city;
    $deliveryAddress->country = $request->country;


    $deliveryAddress->customer_id = $customer->customer_id;
    $deliveryAddress->save();

    return back()->with('success', 'Success message here');
}

Edited again:

It seems from the doc, the refresh() method is as follows:

/**
 * Reload the current model instance with fresh attributes from the database.
 *
 * @return $this
 */
public function refresh()
{
    if (! $this->exists) {
        return $this;
    }

    $this->setRawAttributes(
        static::newQueryWithoutScopes()->findOrFail($this->getKey())->attributes
    );

    $this->load(collect($this->relations)->except('pivot')->keys()->toArray());

    $this->syncOriginal();

    return $this;
}

As you can see from the following line:

static::newQueryWithoutScopes()->findOrFail($this->getKey())->attributes

It will try to find or fail (404) while refreshing the model. I suspect in this case, that it is not able to get the appropriate key and that is why it is failing. I think in this particular case, you will have to get the customer_id from the sequence_customers table.

Maybe you could get away by doing something like the following:

// Assuming SequenceCustomer is the model name
$latest = \App\SequenceCustomer::latest()->first(); 

// and then you would be able to access the latest customer_id by doing the following

$customer_id = $latest->customer_id;

This is obviously not a scalable solution, but I am not very sure how else to solve this particular issue :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM