简体   繁体   English

如何在Laravel中使用雄辩的方式获取最后插入的非增量ID?

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

I have two models Customer and Address . 我有两个模型CustomerAddress My Customer has non incremental primary key and is of type string which is customer_id . 我的Customer具有非增量主键,类型为string ,其值为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很多addresses :发票地址,收货地址,当前地址等。我的Customer模型如下所示:

Customer.php 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 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. 要注意的另一件事是我的customer_id是增量的,因为我已经创建了一个单独的表,即customer_sequence ,该表是自动增量的,在插入记录之前,我使用触发器将其附加两个字符代码,然后将其放置到我的customers表中。 My customer_sequence migration is as shown below 我的customer_sequence迁移如下所示

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: 我用于插入增量字符串id的触发器如下:

Migration for customer_id trigger 迁移customer_id触发器

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 . 现在,当我保存客户数据并尝试从客户模型获取id ,它将返回null My controller is as shown below: 我的控制器如下图所示:

CustomerController.php 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. 当您调用save() ,实际上是在字符串上调用save() Fix it by specifying the fillable properties on your Customer model. 通过在Customer模型上指定可填充属性来对其进行修复。 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. 之后, $customer->customer_id不应为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. 这意味着在创建Customer您还必须提供customer_id ,因为它不再自动递增。

I also took a deeper look at the API. 我还对API进行了更深入的研究。 It seems Laravel won't be aware of the attribute set by the trigger at that stage. 在该阶段,Laravel似乎不会意识到触发器设置的属性。 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 . 您可以尝试refresh()模型,该模型将从数据库中获取新的属性,并假设触发器工作正常,那么您应该找回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. 我怀疑这就是为什么它抛出404的原因,因为未为GET请求定义相同的路由。

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: 从文档看来, refresh()方法如下:

/**
 * 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. 刷新模型时,它将尝试查找或失败(404)。 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. 我认为在这种特殊情况下,您将不得不从sequence_customers表中获取customer_id

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 :) 这显然不是一个可扩展的解决方案,但是我不太确定该如何解决这个问题:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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